1# Copyright 2023 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from __future__ import annotations 6 7import re 8from typing import TYPE_CHECKING, Tuple 9 10from crossbench import plt 11from crossbench.browsers.attributes import BrowserAttributes 12from crossbench.browsers.browser import Browser 13from crossbench.browsers.viewport import Viewport 14from crossbench.browsers.webdriver import WebDriverBrowser 15 16if TYPE_CHECKING: 17 from crossbench.browsers.settings import Settings 18 from crossbench.flags.base import Flags 19 from crossbench.path import AnyPath 20 from crossbench.runner.groups.session import BrowserSessionRunGroup 21 22 23class Firefox(Browser): 24 25 @classmethod 26 def default_path(cls, platform: plt.Platform) -> AnyPath: 27 return platform.search_app_or_executable( 28 "Firefox", 29 macos=["Firefox.app"], 30 linux=["firefox"], 31 win=["Mozilla Firefox/firefox.exe"]) 32 33 @classmethod 34 def developer_edition_path(cls, platform: plt.Platform) -> AnyPath: 35 return platform.search_app_or_executable( 36 "Firefox Developer Edition", 37 macos=["Firefox Developer Edition.app"], 38 linux=["firefox-developer-edition"], 39 win=["Firefox Developer Edition/firefox.exe"]) 40 41 @classmethod 42 def nightly_path(cls, platform: plt.Platform) -> AnyPath: 43 return platform.search_app_or_executable( 44 "Firefox Nightly", 45 macos=["Firefox Nightly.app"], 46 linux=["firefox-nightly", "firefox-trunk"], 47 win=["Firefox Nightly/firefox.exe"]) 48 49 def _setup_cache_dir(self, settings: Settings) -> None: 50 cache_dir = settings.cache_dir 51 if cache_dir: 52 self.cache_dir = cache_dir 53 self.clear_cache_dir = False 54 else: 55 self.cache_dir: AnyPath = settings.platform.mkdtemp(prefix="firefox") 56 self.clear_cache_dir = True 57 58 @property 59 def type_name(self) -> str: 60 return "firefox" 61 62 @property 63 def attributes(self) -> BrowserAttributes: 64 return BrowserAttributes.FIREFOX 65 66 def _extract_version(self) -> str: 67 assert self.path 68 version_string = self.platform.app_version(self.path) 69 # "Firefox 107.0" => "107.0" 70 return str(re.findall(r"[\d\.]+", version_string)[0]) 71 72 def _get_browser_flags_for_session( 73 self, session: BrowserSessionRunGroup) -> Tuple[str, ...]: 74 flags_copy = self.flags.copy() 75 flags_copy.update(session.extra_flags) 76 flags_copy.update(self.network.extra_flags(self.attributes)) 77 self._handle_viewport_flags(flags_copy) 78 if self.log_file: 79 flags_copy["--MOZ_LOG_FILE"] = str(self.log_file) 80 return tuple(flags_copy) 81 82 def _handle_viewport_flags(self, flags: Flags) -> None: 83 new_width, new_height = 0, 0 84 if self.viewport.has_size: 85 new_width, new_height = self.viewport.size 86 update_size = False 87 if "--width" in flags: 88 if self.viewport.is_default: 89 new_width = int(flags["--width"]) 90 update_size = True 91 else: 92 assert self.viewport.width == int(flags["--width"]) 93 if "--height" in flags: 94 if self.viewport.is_default: 95 new_height = int(flags["--height"]) 96 update_size = True 97 else: 98 assert self.viewport.height == int(flags["--height"]) 99 if update_size: 100 assert self.viewport.is_default 101 self.viewport = Viewport(new_width, new_height) 102 elif self.viewport.has_size: 103 flags["--width"] = str(self.viewport.width) 104 flags["--height"] = str(self.viewport.height) 105 106 self._sync_viewport_flag(flags, "--kiosk", self.viewport.is_fullscreen, 107 Viewport.FULLSCREEN) 108 self._sync_viewport_flag(flags, "--headless", self.viewport.is_headless, 109 Viewport.HEADLESS) 110 111 if self.viewport.has_size and not self.viewport.is_default: 112 if not isinstance(self, 113 WebDriverBrowser) and self.viewport.size != (0, 0): 114 raise ValueError(f"Browser {self} cannot handle viewport position: " 115 f"{self.viewport.position}") 116 else: 117 if not isinstance(self, WebDriverBrowser): 118 raise ValueError( 119 f"Browser {self} cannot handle viewport mode: {self.viewport}") 120