svc: hud: Protect window switches with a lock
Any time we need to switch Firefox windows, we need to use a lock to prevent multiple simultaneous requests. If we do not, interleaved Marionette commands may result in performing operations on the wrong window. For example, making two simultaneous requests for screenshots is liable to return the wrong window for one of them.master
parent
35eba74bfd
commit
fa1c9cb42a
|
@ -35,19 +35,21 @@ class HUDService:
|
||||||
self.windows: Dict[str, str] = {}
|
self.windows: Dict[str, str] = {}
|
||||||
|
|
||||||
self.urls_file = Path('urls.json')
|
self.urls_file = Path('urls.json')
|
||||||
|
self.lock = asyncio.Lock()
|
||||||
|
|
||||||
async def get_screens(self) -> Dict[str, HUDScreen]:
|
async def get_screens(self) -> Dict[str, HUDScreen]:
|
||||||
assert self.marionette
|
assert self.marionette
|
||||||
screens = {}
|
screens = {}
|
||||||
for name, handle in self.windows.items():
|
async with self.lock:
|
||||||
await self.marionette.switch_to_window(handle)
|
for name, handle in self.windows.items():
|
||||||
title = await self.marionette.get_title()
|
await self.marionette.switch_to_window(handle)
|
||||||
url = await self.marionette.get_url()
|
title = await self.marionette.get_title()
|
||||||
rect = await self.marionette.get_window_rect()
|
url = await self.marionette.get_url()
|
||||||
screens[name] = HUDScreen(
|
rect = await self.marionette.get_window_rect()
|
||||||
handle=handle, title=title, url=url, dimensions=rect
|
screens[name] = HUDScreen(
|
||||||
)
|
handle=handle, title=title, url=url, dimensions=rect
|
||||||
return screens
|
)
|
||||||
|
return screens
|
||||||
|
|
||||||
async def initialize(self) -> None:
|
async def initialize(self) -> None:
|
||||||
assert self.marionette
|
assert self.marionette
|
||||||
|
@ -56,36 +58,39 @@ class HUDService:
|
||||||
raise NoMonitorConfig(
|
raise NoMonitorConfig(
|
||||||
'Cannot initialize display: No monitor config supplied'
|
'Cannot initialize display: No monitor config supplied'
|
||||||
)
|
)
|
||||||
log.info(
|
async with self.lock:
|
||||||
'Initializing display for %d monitors',
|
log.info(
|
||||||
len(self.monitor_config.monitors),
|
'Initializing display for %d monitors',
|
||||||
)
|
len(self.monitor_config.monitors),
|
||||||
window: Optional[str] = await self.marionette.new_window('window')
|
|
||||||
for handle in await self.marionette.get_window_handles():
|
|
||||||
if handle == window:
|
|
||||||
continue
|
|
||||||
await self.marionette.switch_to_window(handle)
|
|
||||||
await self.marionette.close_window()
|
|
||||||
tasks = []
|
|
||||||
for monitor in self.monitor_config.monitors:
|
|
||||||
try:
|
|
||||||
url = self.urls[monitor.name]
|
|
||||||
except KeyError:
|
|
||||||
continue
|
|
||||||
if window is None:
|
|
||||||
window = await self.marionette.new_window('window')
|
|
||||||
self.windows[monitor.name] = window
|
|
||||||
await self.marionette.switch_to_window(window)
|
|
||||||
await self.marionette.set_window_rect(
|
|
||||||
x=monitor.pos_x,
|
|
||||||
y=1,
|
|
||||||
)
|
)
|
||||||
await self.marionette.fullscreen()
|
window: Optional[str] = await self.marionette.new_window('window')
|
||||||
log.info('Screen %s: Opening URL %s', monitor.name, url)
|
for handle in await self.marionette.get_window_handles():
|
||||||
tasks.append(asyncio.create_task(self.marionette.navigate(url)))
|
if handle == window:
|
||||||
window = None
|
continue
|
||||||
if tasks:
|
await self.marionette.switch_to_window(handle)
|
||||||
await asyncio.wait(tasks)
|
await self.marionette.close_window()
|
||||||
|
tasks = []
|
||||||
|
for monitor in self.monitor_config.monitors:
|
||||||
|
try:
|
||||||
|
url = self.urls[monitor.name]
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
if window is None:
|
||||||
|
window = await self.marionette.new_window('window')
|
||||||
|
self.windows[monitor.name] = window
|
||||||
|
await self.marionette.switch_to_window(window)
|
||||||
|
await self.marionette.set_window_rect(
|
||||||
|
x=monitor.pos_x,
|
||||||
|
y=1,
|
||||||
|
)
|
||||||
|
await self.marionette.fullscreen()
|
||||||
|
log.info('Screen %s: Opening URL %s', monitor.name, url)
|
||||||
|
tasks.append(
|
||||||
|
asyncio.create_task(self.marionette.navigate(url))
|
||||||
|
)
|
||||||
|
window = None
|
||||||
|
if tasks:
|
||||||
|
await asyncio.wait(tasks)
|
||||||
|
|
||||||
def load_urls(self) -> None:
|
def load_urls(self) -> None:
|
||||||
try:
|
try:
|
||||||
|
@ -113,8 +118,9 @@ class HUDService:
|
||||||
|
|
||||||
async def refresh_screen(self, name: str) -> None:
|
async def refresh_screen(self, name: str) -> None:
|
||||||
assert self.marionette
|
assert self.marionette
|
||||||
await self.marionette.switch_to_window(self.windows[name])
|
async with self.lock:
|
||||||
await self.marionette.refresh()
|
await self.marionette.switch_to_window(self.windows[name])
|
||||||
|
await self.marionette.refresh()
|
||||||
|
|
||||||
async def set_display(self, host: str, port: int) -> None:
|
async def set_display(self, host: str, port: int) -> None:
|
||||||
if self.marionette:
|
if self.marionette:
|
||||||
|
@ -138,6 +144,7 @@ class HUDService:
|
||||||
async def take_screenshot(self, screen: str) -> bytes:
|
async def take_screenshot(self, screen: str) -> bytes:
|
||||||
assert self.marionette
|
assert self.marionette
|
||||||
handle = self.windows[screen]
|
handle = self.windows[screen]
|
||||||
await self.marionette.switch_to_window(handle)
|
async with self.lock:
|
||||||
data = await self.marionette.take_screenshot()
|
await self.marionette.switch_to_window(handle)
|
||||||
|
data = await self.marionette.take_screenshot()
|
||||||
return base64.b64decode(data)
|
return base64.b64decode(data)
|
||||||
|
|
Loading…
Reference in New Issue