source: src/mlx/gui/cef.py@ 703:df10e569b029

cef
Last change on this file since 703:df10e569b029 was 703:df10e569b029, checked in by István Váradi <ivaradi@…>, 7 years ago

The getting of the XML link is also protected by an exception handler (re #279).

File size: 13.7 KB
Line 
1from common import *
2
3from mava_simbrief import MavaSimbriefIntegrator
4
5from mlx.util import secondaryInstallation
6
7from cefpython3 import cefpython
8from selenium import webdriver
9from selenium.webdriver.chrome.options import Options
10
11import platform
12import json
13import time
14import os
15import re
16import threading
17import tempfile
18import traceback
19
20#------------------------------------------------------------------------------
21
22## @package mlx.gui.cef
23#
24# Some helper stuff related to the Chrome Embedded Framework
25
26#------------------------------------------------------------------------------
27
28# Indicate if we should quit
29_toQuit = False
30
31# The Selenium thread
32_seleniumHandler = None
33
34#------------------------------------------------------------------------------
35
36def getArgsFilePath():
37 """Get the path of the argument file."""
38 if os.name=="nt":
39 return os.path.join(tempfile.gettempdir(),
40 "mlxcef.args" +
41 (".secondary" if secondaryInstallation else ""))
42 else:
43 import pwd
44 return os.path.join(tempfile.gettempdir(),
45 "mlxcef." + pwd.getpwuid(os.getuid())[0] + ".args" +
46 (".secondary" if secondaryInstallation else ""))
47
48#------------------------------------------------------------------------------
49
50class ArgsFileWaiter(threading.Thread):
51 """A thread to wait for the appearance of the arguments file."""
52 def __init__(self, initializedCallback):
53 """Construct the thread."""
54 threading.Thread.__init__(self)
55 self.daemon = True
56
57 self._initializedCallback = initializedCallback
58
59 def run(self):
60 """Repeatedly check for the existence of the arguments file.
61
62 If it is found, read it, extract the arguments and insert a job into
63 the GUI loop to perform the actual initialization of CEF."""
64 argsFilePath = getArgsFilePath()
65 print "Waiting for the arguments file '%s' to appear" % (argsFilePath,)
66
67 while not os.path.exists(argsFilePath):
68 time.sleep(0.1)
69
70 print "Got arguments, reading them."""
71
72 with open(argsFilePath, "rt") as f:
73 args = f.read().split()
74
75 gobject.idle_add(_initializeCEF, args, self._initializedCallback)
76
77#------------------------------------------------------------------------------
78
79SIMBRIEF_PROGRESS_SEARCHING_BROWSER = MavaSimbriefIntegrator.PROGRESS_SEARCHING_BROWSER
80SIMBRIEF_PROGRESS_LOADING_FORM = MavaSimbriefIntegrator.PROGRESS_LOADING_FORM
81SIMBRIEF_PROGRESS_FILLING_FORM = MavaSimbriefIntegrator.PROGRESS_FILLING_FORM
82SIMBRIEF_PROGRESS_WAITING_LOGIN = MavaSimbriefIntegrator.PROGRESS_WAITING_LOGIN
83SIMBRIEF_PROGRESS_LOGGING_IN = MavaSimbriefIntegrator.PROGRESS_LOGGING_IN
84SIMBRIEF_PROGRESS_WAITING_RESULT = MavaSimbriefIntegrator.PROGRESS_WAITING_RESULT
85
86SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING = MavaSimbriefIntegrator.PROGRESS_MAX + 1
87SIMBRIEF_PROGRESS_DONE = 1000
88
89SIMBRIEF_RESULT_NONE = MavaSimbriefIntegrator.RESULT_NONE
90SIMBRIEF_RESULT_OK = MavaSimbriefIntegrator.RESULT_OK
91SIMBRIEF_RESULT_ERROR_OTHER = MavaSimbriefIntegrator.RESULT_ERROR_OTHER
92SIMBRIEF_RESULT_ERROR_NO_FORM = MavaSimbriefIntegrator.RESULT_ERROR_NO_FORM
93SIMBRIEF_RESULT_ERROR_NO_POPUP = MavaSimbriefIntegrator.RESULT_ERROR_NO_POPUP
94SIMBRIEF_RESULT_ERROR_LOGIN_FAILED = MavaSimbriefIntegrator.RESULT_ERROR_LOGIN_FAILED
95
96#------------------------------------------------------------------------------
97
98class SeleniumHandler(threading.Thread):
99 """Thread to handle Selenium operations."""
100 def __init__(self, programDirectory):
101 """Construct the thread."""
102 threading.Thread.__init__(self)
103 self.daemon = False
104
105 self._programDirectory = programDirectory
106
107 self._commandsCondition = threading.Condition()
108 self._commands = []
109
110 self._driver = None
111
112 self._simBriefBrowser = None
113
114 self._toQuit = False
115
116 @property
117 def programDirectory(self):
118 """Get the program directory."""
119 return self._programDirectory
120
121 @property
122 def simBriefInitURL(self):
123 """Get the initial URL for the SimBrief browser."""
124 return "file://" + os.path.join(self.programDirectory, "simbrief.html")
125
126 def run(self):
127 """Create the Selenium driver and the perform any operations
128 requested."""
129 scriptName = "mlx_cef_caller"
130 if secondaryInstallation:
131 scriptName += "_secondary"
132 scriptName += ".bat" if os.name=="nt" else ".sh"
133
134 scriptPath = os.path.join(self._programDirectory, scriptName)
135 print "Creating the Selenium driver to call script", scriptPath
136
137 options = Options()
138 options.binary_location = scriptPath
139 driver = self._driver = webdriver.Chrome(chrome_options = options)
140 # try:
141 # except:
142 # traceback.print_exc()
143
144 print "Created Selenium driver."
145 while not self._toQuit:
146 with self._commandsCondition:
147 while not self._commands:
148 self._commandsCondition.wait()
149
150 command = self._commands[0]
151 del self._commands[0]
152
153 command()
154
155 driver.quit()
156
157 def initializeSimBrief(self):
158 """Create and initialize the browser used for Simbrief."""
159 windowInfo = cefpython.WindowInfo()
160 windowInfo.SetAsOffscreen(int(0))
161
162 url = self.simBriefInitURL
163 self._simBriefBrowser = \
164 cefpython.CreateBrowserSync(windowInfo, browserSettings = {},
165 navigateUrl = self.simBriefInitURL)
166 self._simBriefBrowser.SetClientHandler(OffscreenRenderHandler())
167 self._simBriefBrowser.SetFocus(True)
168
169 def callSimBrief(self, plan, getCredentials, updateProgress,
170 htmlFilePath):
171 """Call SimBrief with the given plan."""
172 self._enqueue(lambda:
173 self._callSimBrief(plan, self._driver,
174 getCredentials, updateProgress,
175 htmlFilePath))
176
177 def quit(self):
178 """Instruct the thread to quit and then join it."""
179 self._enqueue(self._quit)
180 self.join()
181
182 def _enqueue(self, command):
183 """Enqueue the given command.
184
185 command should be a function to be executed in the thread."""
186 with self._commandsCondition:
187 self._commands.append(command)
188 self._commandsCondition.notify()
189
190 def _callSimBrief(self, plan, driver,
191 getCredentials, updateProgress, htmlFilePath):
192 """Perform the SimBrief call."""
193 self._simBriefBrowser.LoadUrl(self.simBriefInitURL)
194
195 integrator = MavaSimbriefIntegrator(plan = plan, driver = driver)
196 link = None
197 try:
198 link = integrator.get_xml_link(getCredentials, updateProgress,
199 local_xml_debug = False,
200 local_html_debug = False)
201 except Exception, e:
202 print "Failed to initiate the generation of the briefing:", e
203 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
204 SIMBRIEF_RESULT_ERROR_OTHER, None)
205
206 if link is not None:
207 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
208 SIMBRIEF_RESULT_NONE, None)
209
210 try:
211 flight_info = integrator.get_results(link,
212 html_file_path =
213 htmlFilePath)
214
215 updateProgress(SIMBRIEF_PROGRESS_DONE,
216 SIMBRIEF_RESULT_OK, flight_info)
217 except Exception, e:
218 print "Failed to retrieve the briefing:", e
219 updateProgress(SIMBRIEF_PROGRESS_RETRIEVING_BRIEFING,
220 SIMBRIEF_RESULT_ERROR_OTHER, None)
221
222 def _quit(self):
223 """Set the _toQuit member variable to indicate that the thread should
224 quit."""
225 self._toQuit = True
226
227#------------------------------------------------------------------------------
228
229def initialize(programDirectory, initializedCallback):
230 """Initialize the Chrome Embedded Framework."""
231 global _toQuit, _seleniumHandler
232 _toQuit = False
233
234 gobject.threads_init()
235
236 argsFilePath = getArgsFilePath()
237 try:
238 os.unlink(argsFilePath)
239 except:
240 pass
241
242 _seleniumHandler = SeleniumHandler(programDirectory)
243 _seleniumHandler.start()
244
245 ArgsFileWaiter(initializedCallback).start()
246
247#------------------------------------------------------------------------------
248
249def _initializeCEF(args, initializedCallback):
250 """Perform the actual initialization of CEF using the given arguments."""
251 print "Initializing CEF with args:", args
252
253 settings = {
254 "debug": True, # cefpython debug messages in console and in log_file
255 "log_severity": cefpython.LOGSEVERITY_VERBOSE, # LOGSEVERITY_VERBOSE
256 "log_file": "", # Set to "" to disable
257 "release_dcheck_enabled": True, # Enable only when debugging
258 # This directories must be set on Linux
259 "locales_dir_path": os.path.join(cefpython.GetModuleDirectory(), "locales"),
260 "resources_dir_path": cefpython.GetModuleDirectory(),
261 "browser_subprocess_path": "%s/%s" % \
262 (cefpython.GetModuleDirectory(), "subprocess"),
263 }
264
265 switches={}
266 for arg in args:
267 if arg.startswith("--"):
268 if arg != "--enable-logging":
269 assignIndex = arg.find("=")
270 if assignIndex<0:
271 switches[arg[2:]] = ""
272 else:
273 switches[arg[2:assignIndex]] = arg[assignIndex+1:]
274 else:
275 print "Unhandled switch", arg
276
277 cefpython.Initialize(settings, switches)
278
279 gobject.timeout_add(10, _handleTimeout)
280
281 print "Initialized, executing callback..."
282 initializedCallback()
283
284#------------------------------------------------------------------------------
285
286def getContainer():
287 """Get a container object suitable for running a browser instance
288 within."""
289 if os.name=="nt":
290 container = gtk.DrawingArea()
291 container.set_property("can-focus", True)
292 container.connect("size-allocate", _handleSizeAllocate)
293 else:
294 container = gtk.VBox(True, 0)
295
296 container.show()
297
298 return container
299
300#------------------------------------------------------------------------------
301
302def startInContainer(container, url, browserSettings = {}):
303 """Start a browser instance in the given container with the given URL."""
304 if os.name=="nt":
305 windowID = container.get_window().handle
306 else:
307 m = re.search("GtkVBox at 0x(\w+)", str(container))
308 hexID = m.group(1)
309 windowID = int(hexID, 16)
310
311 windowInfo = cefpython.WindowInfo()
312 windowInfo.SetAsChild(windowID)
313
314 return cefpython.CreateBrowserSync(windowInfo,
315 browserSettings = browserSettings,
316 navigateUrl = url)
317
318#------------------------------------------------------------------------------
319
320class OffscreenRenderHandler(object):
321 def GetRootScreenRect(self, browser, rect):
322 #print "GetRootScreenRect"
323 rect += [0, 0, 800, 600]
324 return True
325
326 def GetViewRect(self, browser, rect):
327 #print "GetViewRect"
328 rect += [0, 0, 800, 600]
329 return True
330
331 def GetScreenPoint(self, browser, viewX, viewY, screenCoordinates):
332 #print "GetScreenPoint", viewX, viewY
333 rect += [viewX, viewY]
334 return True
335
336 def GetScreenInfo(self, browser, screenInfo):
337 #print "GetScreenInfo"
338 pass
339
340 def OnPopupShow(self, browser, show):
341 #print "OnPopupShow", show
342 pass
343
344 def OnPopupSize(self, browser, rect):
345 #print "OnPopupSize", rect
346 pass
347
348 def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
349 #print "OnPaint", paintElementType, dirtyRects, buffer, width, height
350 pass
351
352 def OnCursorChange(self, browser, cursor):
353 #print "OnCursorChange", cursor
354 pass
355
356 def OnScrollOffsetChanged(self, browser):
357 #print "OnScrollOffsetChange"
358 pass
359
360 def OnBeforePopup(self, browser, frame, targetURL, targetFrameName,
361 popupFeatures, windowInfo, client, browserSettings,
362 noJavascriptAccess):
363 wInfo = cefpython.WindowInfo()
364 wInfo.SetAsOffscreen(int(0))
365
366 windowInfo.append(wInfo)
367
368 return False
369
370#------------------------------------------------------------------------------
371
372def initializeSimBrief():
373 """Initialize the (hidden) browser window for SimBrief."""
374 _seleniumHandler.initializeSimBrief()
375
376#------------------------------------------------------------------------------
377
378def callSimBrief(plan, getCredentials, updateProgress, htmlFilePath):
379 """Call SimBrief with the given plan."""
380 _seleniumHandler.callSimBrief(plan, getCredentials,
381 updateProgress, htmlFilePath)
382
383#------------------------------------------------------------------------------
384
385def finalize():
386 """Finalize the Chrome Embedded Framework."""
387 global _toQuit, _seleniumHandler
388 toQuit = True
389 cefpython.Shutdown()
390 _seleniumHandler.quit()
391
392#------------------------------------------------------------------------------
393
394def _handleTimeout():
395 """Handle the timeout by running the CEF message loop."""
396 if _toQuit:
397 return False
398 else:
399 cefpython.MessageLoopWork()
400 return True
401
402#------------------------------------------------------------------------------
403
404def _handleSizeAllocate(widget, sizeAlloc):
405 """Handle the size-allocate event."""
406 cefpython.WindowUtils.OnSize(widget.get_window().handle, 0, 0, 0)
Note: See TracBrowser for help on using the repository browser.