Source code for fortrace.utility.applications.web_browsers.web_browser

import os
from time import sleep
from typing import Literal
from urllib.parse import unquote

import urllib3

from fortrace.core.qemu_monitor import QEMUMonitorSession
from fortrace.utility.applications.application import (
    ApplicationEvent,
    ApplicationType,
    FileDialogue,
    GenericApplication,
    ParentNotifier,
    PopupType,
)
from fortrace.utility.image_processing.image_similarity import nrmse
from fortrace.utility.logger_helper import setup_logger

logger = setup_logger(__name__)


[docs] class GenericWebBrowser(GenericApplication): """Representation of a generic Web browser, providing basic functionality.""" _tabs = list[str] def __init__( self, name: str, qs: QEMUMonitorSession, parent_notifier: ParentNotifier ): super().__init__(name, ApplicationType.WEB_BROWSER, qs, parent_notifier) # TODO: think about tab identifier, instead of empty string/ tab dataclass? self._tabs = [""] # contains tabs in correct order, start with one opened tab self._active_tab = 0 def _new_tab(self): self._qs.send_key_combination("ctrl-t") self._tabs.append("") self._active_tab = len(self._tabs) - 1 sleep(1)
[docs] def close_tab(self): """Close focused tab""" self._qs.send_key_combination("ctrl-w") self._tabs.remove(self._tabs[self._active_tab]) # deleted tab was rightmost tab if self._active_tab == len(self._tabs): self._active_tab -= 1 # if last tab is closed application will close and must notify desktop_env if len(self._tabs) == 0: self._parent_notifier(ApplicationEvent.CLOSED, application_reference=self)
def _focus_address_bar(self): self._qs.send_key_combination("ctrl-l") sleep(0.5)
[docs] def focus_tab(self, url: str): """Set focus on tab. Args: url: the url of the tab to be focused (the parameter is matched against all members of _active_tabs. The first substring match is selected) """ new_active_tab_idx = 0 for idx, tab_url in enumerate(self._tabs): if url in tab_url: new_active_tab_idx = idx self._goto_tab(new_active_tab_idx)
[docs] def go_back(self): """Go back one page.""" self._qs.send_key_combination("alt-left")
[docs] def go_forward(self): """Go forward one page.""" self._qs.send_key_combination("alt-right")
def _goto_tab(self, idx: int): """Go to the opened tab with the specified index. Args: idx: index of the tab (starting at 1) """ if 1 <= idx <= 8: self._qs.send_key_combination(f"alt-{idx}") else: raise NotImplementedError(f"Can't switch to tab with index {idx}") self._active_tab = idx
[docs] def browse_to_url( self, url: str | urllib3.util.Url, new_tab: bool = False, ): """Browse to the provided URL or download file behind URL If you provide the URL pointing directly to the file, most browsers will download it. Args: url: to destination url #TODO: check whether str is needed new_tab: open it in a new tab. If yes, the focus will be on the new tab """ if new_tab: self._new_tab() before = self.take_screenshot() self._focus_address_bar() self._qs.send_text( unquote(url) if isinstance(url, str) else unquote(url.url), True ) for _ in range(10): sleep(5) # wait for page to load score = nrmse(before, self.take_screenshot()) if score >= 0.25: logger.debug("Consider side as loaded, since screenshots differ enough") break else: logger.warning( "After more than 50 seconds the screenshots still look very similar" " (NRMSE: %s). This might be due to the page %s failed to load.", score, str(url), )
[docs] def save_to_favorites(self): """Bookmarks/'saves to favourites' current page of active tab.""" self._qs.send_key_combination("ctrl-d") self._qs.send_key_combination("ret")
[docs] def save(self, destination: os.PathLike | None = None, name: str | None = None): """Save the current page. Args: destination: path to save file to (defaults to downloads directory) name: can be provided if the file should be renamed """ before = self._qs.take_screenshot() self._qs.send_key_combination("ctrl-s") saving_dialogue = self.open_popup( before, PopupType.FILE_DIALOGUE ) # type:FileDialogue saving_dialogue.save_to_directory(destination, name)
[docs] def remove_from_favorite(self): """Removes current page from bookmarks/favourites.""" self._qs.send_key_combination("ctrl-d") self._qs.send_key_combination("tab", 3) self._qs.send_key_combination("ret")
@property def active_tab(self): return self._tabs[self._active_tab]