1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import subprocess
from enum import Enum
from odoo.addons.hw_drivers.tools import helpers
_logger = logging.getLogger(__name__)
MIN_IMAGE_VERSION = 24.10
CHROMIUM_ARGS = [
'--incognito',
'--disable-infobars',
'--noerrdialogs',
'--no-first-run',
'--bwsi', # Use chromium without signing in
'--disable-extensions', # Disable extensions as they fill up /tmp
'--disk-cache-dir=/dev/null', # Disable disk cache
'--disk-cache-size=1', # Set disk cache size to 1 byte
'--log-level=3', # Reduce amount of logs
]
class BrowserState(Enum):
"""Enum to represent the state of the browser"""
NORMAL = 'normal'
KIOSK = 'kiosk'
FULLSCREEN = 'fullscreen'
class Browser:
"""Methods to interact with a browser"""
def __init__(self, url, _x_screen, env):
"""
:param url: URL to open in the browser
:param _x_screen: X screen number
:param env: Environment variables (e.g. os.environ.copy())
:param kiosk: Whether the browser should be in kiosk mode
"""
self.url = url
# helpers.get_version returns a string formatted as: <L|W><version> (L: Linux, W: Windows)
self.browser = 'chromium-browser' if float(helpers.get_version()[1:]) >= MIN_IMAGE_VERSION else 'firefox'
self.browser_process_name = 'chromium' if self.browser == 'chromium-browser' else self.browser
self.state = BrowserState.NORMAL
self._x_screen = _x_screen
self._set_environment(env)
def _set_environment(self, env):
"""
Set the environment variables for the browser
:param env: Environment variables (os.environ.copy())
"""
self.env = env
self.env['DISPLAY'] = f':0.{self._x_screen}'
self.env['XAUTHORITY'] = '/run/lightdm/pi/xauthority'
for key in ['HOME', 'XDG_RUNTIME_DIR', 'XDG_CACHE_HOME']:
self.env[key] = '/tmp/' + self._x_screen
def open_browser(self, url=None, state=BrowserState.NORMAL):
"""
open the browser with the given URL, or reopen it if it is already open
:param url: URL to open in the browser
:param state: State of the browser (normal, kiosk, fullscreen)
"""
self.url = url or self.url
self.state = state
# Reopen to take new url or additional args into account
self.close_browser()
browser_args = list(CHROMIUM_ARGS) if self.browser == 'chromium-browser' else []
if state == BrowserState.KIOSK:
browser_args.append("--kiosk")
elif state == BrowserState.FULLSCREEN:
browser_args.append("--start-fullscreen")
subprocess.Popen(
[
self.browser,
self.url,
*browser_args,
],
env=self.env,
)
if self.browser == 'firefox' and state == BrowserState.FULLSCREEN:
# Firefox does not support fullscreen via command line argument, so we use a keypress
self.xdotool_keystroke('F11')
helpers.save_browser_state(url=self.url)
def close_browser(self):
"""close the browser"""
# Kill browser instance (can't `instance.pkill()` as we can't keep the instance after Odoo service restarts)
# We need to terminate it because Odoo will create a new instance each time it is restarted.
subprocess.run(['pkill', self.browser_process_name], check=False)
def xdotool_keystroke(self, keystroke):
"""
Execute a keystroke using xdotool
:param keystroke: Keystroke to execute
"""
subprocess.run([
'xdotool', 'search',
'--sync', '--onlyvisible',
'--screen', self._x_screen,
'--class', self.browser_process_name,
'key', keystroke,
], check=False)
def xdotool_type(self, text):
"""
Type text using xdotool
:param text: Text to type
"""
subprocess.run([
'xdotool', 'search',
'--sync', '--onlyvisible',
'--screen', self._x_screen,
'--class', self.browser_process_name,
'type', text,
], check=False)
def open_new_tab(self, url):
"""
Open a new tab with the given URL
:param url: URL to open in the new tab
"""
self.url = url
self.xdotool_keystroke('ctrl+t')
self.xdotool_type(self.url)
self.xdotool_keystroke('Return')
def refresh(self):
"""Refresh the current tab"""
self.xdotool_keystroke('ctrl+r')
def disable_kiosk_mode(self):
"""Removes arguments to chromium-browser cli to open it without kiosk mode"""
if self.state == BrowserState.KIOSK:
self.open_browser(state=BrowserState.FULLSCREEN)
|