from common import * from mlx.util import secondaryInstallation from cefpython3 import cefpython import platform import json import time import os import re import thread import threading import tempfile import traceback import urllib2 from lxml import etree from StringIO import StringIO import lxml.html #------------------------------------------------------------------------------ ## @package mlx.gui.cef # # Some helper stuff related to the Chrome Embedded Framework #------------------------------------------------------------------------------ # Indicate if we should quit _toQuit = False # The SimBrief handler _simBriefHandler = None #------------------------------------------------------------------------------ SIMBRIEF_PROGRESS_SEARCHING_BROWSER = 1 SIMBRIEF_PROGRESS_LOADING_FORM = 2 SIMBRIEF_PROGRESS_FILLING_FORM = 3 SIMBRIEF_PROGRESS_WAITING_LOGIN = 4 SIMBRIEF_PROGRESS_LOGGING_IN = 5 SIMBRIEF_PROGRESS_WAITING_RESULT = 6 SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING = 7 SIMBRIEF_PROGRESS_DONE = 1000 SIMBRIEF_RESULT_NONE = 0 SIMBRIEF_RESULT_OK = 1 SIMBRIEF_RESULT_ERROR_OTHER = 2 SIMBRIEF_RESULT_ERROR_NO_FORM = 11 SIMBRIEF_RESULT_ERROR_NO_POPUP = 12 SIMBRIEF_RESULT_ERROR_LOGIN_FAILED = 13 #------------------------------------------------------------------------------ class SimBriefHandler(object): """An object to store the state of a SimBrief query.""" _formURL = "http://flare.privatedns.org/mava_simbrief/simbrief_form.html" _querySettings = { 'navlog': True, 'etops': True, 'stepclimbs': True, 'tlr': True, 'notams': True, 'firnot': True, 'maps': 'Simple', }; def __init__(self): """Construct the handler.""" self._browser = None self._plan = None self._getCredentials = None self._getCredentialsCount = 0 self._updateProgressFn = None self._htmlFilePath = None self._lastProgress = SIMBRIEF_PROGRESS_SEARCHING_BROWSER self._timeoutID = None def initialize(self): """Create and initialize the browser used for Simbrief.""" windowInfo = cefpython.WindowInfo() windowInfo.SetAsOffscreen(int(0)) self._browser = \ cefpython.CreateBrowserSync(windowInfo, browserSettings = {}, navigateUrl = SimBriefHandler._formURL) self._browser.SetClientHandler(OffscreenRenderHandler()) self._browser.SetFocus(True) self._browser.SetClientCallback("OnLoadEnd", self._onLoadEnd) bindings = cefpython.JavascriptBindings(bindToFrames=True, bindToPopups=True) bindings.SetFunction("briefingData", self._briefingDataAvailable) bindings.SetFunction("formFilled", self._formFilled) self._browser.SetJavascriptBindings(bindings) def call(self, plan, getCredentials, updateProgress, htmlFilePath): """Call SimBrief with the given plan.""" self._timeoutID = gobject.timeout_add(120*1000, self._timedOut) self._plan = plan self._getCredentials = getCredentials self._getCredentialsCount = 0 self._updateProgressFn = updateProgress self._htmlFilePath = htmlFilePath self._browser.LoadUrl(SimBriefHandler._formURL) self._updateProgress(SIMBRIEF_PROGRESS_LOADING_FORM, SIMBRIEF_RESULT_NONE, None) def _onLoadEnd(self, browser, frame, httpCode): """Called when a page has been loaded in the SimBrief browser.""" url = frame.GetUrl() print "gui.cef.SimBriefHandler._onLoadEnd", httpCode, url if httpCode>=300: self._updateProgress(self._lastProgress, SIMBRIEF_RESULT_ERROR_OTHER, None) elif url.startswith("http://flare.privatedns.org/mava_simbrief/simbrief_form.html"): if self._plan is None: return self._updateProgress(SIMBRIEF_PROGRESS_FILLING_FORM, SIMBRIEF_RESULT_NONE, None) js = "form=document.getElementById(\"sbapiform\");" for (name, value) in self._plan.iteritems(): js += "form." + name + ".value=\"" + value + "\";" for (name, value) in SimBriefHandler._querySettings.iteritems(): if isinstance(value, bool): js += "form." + name + ".checked=" + \ ("true" if value else "false") + ";" elif isinstance(value, str): js += "form." + name + ".value=\"" + value + "\";" js += "form.submitform.click();" js += "window.formFilled();" frame.ExecuteJavascript(js) elif url.startswith("http://www.simbrief.com/system/login.api.php"): (user, password) = self._getCredentials(self._getCredentialsCount) if user is None or password is None: self._updateProgress(SIMBRIEF_PROGRESS_WAITING_LOGIN, SIMBRIEF_RESULT_ERROR_LOGIN_FAILED, None) return self._getCredentialsCount += 1 js = "form=document.forms[0];" js += "form.user.value=\"" + user + "\";" js += "form.pass.value=\"" + password + "\";" js += "form.submit();" frame.ExecuteJavascript(js) elif url.startswith("http://www.simbrief.com/ofp/ofp.loader.api.php"): self._updateProgress(SIMBRIEF_PROGRESS_WAITING_RESULT, SIMBRIEF_RESULT_NONE, None) elif url.startswith("http://flare.privatedns.org/mava_simbrief/simbrief_briefing.php"): js = "form=document.getElementById(\"hiddenform\");" js += "window.briefingData(form.hidden_is_briefing_available.value, form.hidden_link.value);"; frame.ExecuteJavascript(js) def _formFilled(self): """Called when the form has been filled and submitted.""" self._updateProgress(SIMBRIEF_PROGRESS_WAITING_LOGIN, SIMBRIEF_RESULT_NONE, None) def _briefingDataAvailable(self, available, link): """Called when the briefing data is available.""" if available: link ="http://www.simbrief.com/ofp/flightplans/xml/" + link + ".xml" self._updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING, SIMBRIEF_RESULT_NONE, None) thread = threading.Thread(target = self._getResults, args = (link,)) thread.daemon = True thread.start() else: self._updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING, SIMBRIEF_RESULT_ERROR_OTHER, None) def _resultsAvailable(self, flightInfo): """Called from the result retrieval thread when the result is available. It checks for the plan being not None, as we may time out.""" if self._plan is not None: self._updateProgress(SIMBRIEF_PROGRESS_DONE, SIMBRIEF_RESULT_OK, flightInfo) def _updateProgress(self, progress, results, flightInfo): """Update the progress.""" self._lastProgress = progress if results!=SIMBRIEF_RESULT_NONE: gobject.source_remove(self._timeoutID) self._plan = None self._updateProgressFn(progress, results, flightInfo) def _timedOut(self): """Called when the timeout occurs.""" if self._lastProgress==SIMBRIEF_PROGRESS_LOADING_FORM: result = SIMBRIEF_RESULT_ERROR_NO_FORM elif self._lastProgress==SIMBRIEF_PROGRESS_WAITING_LOGIN: result = SIMBRIEF_RESULT_ERROR_NO_POPUP else: result = SIMBRIEF_RESULT_ERROR_OTHER self._updateProgress(self._lastProgress, result, None) return False def _getResults(self, link): """Get the result from the given link.""" availableInfo = {} ## Holds analysis data to be used flightInfo = {} # Obtaining the xml response = urllib2.urlopen(link) xmlContent = response.read() # Processing xml content = etree.iterparse(StringIO(xmlContent)) for (action, element) in content: # Processing tags that occur multiple times if element.tag == "weather": weatherElementList = list(element) for weatherElement in weatherElementList: flightInfo[weatherElement.tag] = weatherElement.text else: availableInfo[element.tag] = element.text # Processing plan_html ## Obtaining chart links imageLinks = [] for imageLinkElement in lxml.html.find_class(availableInfo["plan_html"], "ofpmaplink"): for imageLink in imageLinkElement.iterlinks(): if imageLink[1] == 'src': imageLinks.append(imageLink[2]) flightInfo["image_links"] = imageLinks print(sorted(availableInfo.keys())) htmlFilePath = "simbrief_plan.html" if self._htmlFilePath is None \ else self._htmlFilePath with open(htmlFilePath, 'w') as f: f.write(availableInfo["plan_html"]) gobject.idle_add(self._resultsAvailable, flightInfo) #------------------------------------------------------------------------------ def initialize(initializedCallback): """Initialize the Chrome Embedded Framework.""" global _toQuit, _simBriefHandler _toQuit = False gobject.threads_init() _simBriefHandler = SimBriefHandler() _initializeCEF([], initializedCallback) #------------------------------------------------------------------------------ def _initializeCEF(args, initializedCallback): """Perform the actual initialization of CEF using the given arguments.""" print "Initializing CEF with args:", args settings = { "debug": True, # cefpython debug messages in console and in log_file "log_severity": cefpython.LOGSEVERITY_VERBOSE, # LOGSEVERITY_VERBOSE "log_file": "", # Set to "" to disable "release_dcheck_enabled": True, # Enable only when debugging # This directories must be set on Linux "locales_dir_path": os.path.join(cefpython.GetModuleDirectory(), "locales"), "resources_dir_path": cefpython.GetModuleDirectory(), "browser_subprocess_path": "%s/%s" % \ (cefpython.GetModuleDirectory(), "subprocess"), } switches={} for arg in args: if arg.startswith("--"): if arg != "--enable-logging": assignIndex = arg.find("=") if assignIndex<0: switches[arg[2:]] = "" else: switches[arg[2:assignIndex]] = arg[assignIndex+1:] else: print "Unhandled switch", arg cefpython.Initialize(settings, switches) gobject.timeout_add(10, _handleTimeout) print "Initialized, executing callback..." initializedCallback() #------------------------------------------------------------------------------ def getContainer(): """Get a container object suitable for running a browser instance within.""" if os.name=="nt": container = gtk.DrawingArea() container.set_property("can-focus", True) container.connect("size-allocate", _handleSizeAllocate) else: container = gtk.DrawingArea() container.show() return container #------------------------------------------------------------------------------ def startInContainer(container, url, browserSettings = {}): """Start a browser instance in the given container with the given URL.""" if os.name=="nt": window = container.get_window() if window is None: print "mlx.gui.cef.startInContainer: no window found!" windowID = None else: windowID = window.handle else: windowID = container.window.xid windowInfo = cefpython.WindowInfo() if windowID is not None: windowInfo.SetAsChild(windowID) return cefpython.CreateBrowserSync(windowInfo, browserSettings = browserSettings, navigateUrl = url) #------------------------------------------------------------------------------ class OffscreenRenderHandler(object): def GetRootScreenRect(self, browser, rect): #print "GetRootScreenRect" rect += [0, 0, 800, 600] return True def GetViewRect(self, browser, rect): #print "GetViewRect" rect += [0, 0, 800, 600] return True def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates): #print "GetScreenPoint", viewX, viewY rect += [viewX, viewY] return True def GetScreenInfo(self, browser, screenInfo): #print "GetScreenInfo" pass def OnPopupShow(self, browser, show): #print "OnPopupShow", show pass def OnPopupSize(self, browser, rect): #print "OnPopupSize", rect pass def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height): #print "OnPaint", paintElementType, dirtyRects, buffer, width, height pass def OnCursorChange(self, browser, cursor): #print "OnCursorChange", cursor pass def OnScrollOffsetChanged(self, browser): #print "OnScrollOffsetChange" pass def OnBeforePopup(self, browser, frame, targetURL, targetFrameName, popupFeatures, windowInfo, client, browserSettings, noJavascriptAccess): wInfo = cefpython.WindowInfo() wInfo.SetAsOffscreen(int(0)) windowInfo.append(wInfo) return False #------------------------------------------------------------------------------ def initializeSimBrief(): """Initialize the (hidden) browser window for SimBrief.""" _simBriefHandler.initialize() #------------------------------------------------------------------------------ def callSimBrief(plan, getCredentials, updateProgress, htmlFilePath): """Call SimBrief with the given plan. The callbacks will be called in the main GUI thread.""" _simBriefHandler.call(plan, getCredentials, updateProgress, htmlFilePath) #------------------------------------------------------------------------------ def finalize(): """Finalize the Chrome Embedded Framework.""" global _toQuit _toQuit = True cefpython.Shutdown() #------------------------------------------------------------------------------ def _handleTimeout(): """Handle the timeout by running the CEF message loop.""" if _toQuit: return False else: cefpython.MessageLoopWork() return True #------------------------------------------------------------------------------ def _handleSizeAllocate(widget, sizeAlloc): """Handle the size-allocate event.""" window = widget.get_window() if widget is not None: cefpython.WindowUtils.OnSize(window.handle, 0, 0, 0)