From aff0d509725893624b9a27766df3d1727b34ab40 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Mon, 14 Sep 2026 04:39:21 -0400 Subject: [PATCH] =?UTF-8?q?[FIX]=20selenium=20:=20accorder=20le=20priv?= =?UTF-8?q?=C3=A9=20aux=20extensions=20sans=20about:addons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Firefox refuse la navigation vers about:addons depuis le contexte contenu, et le contexte chrome exige -remote-allow-system-access, que geckodriver refuse via les capabilities : les deux voies vers la case « Exécuter dans les fenêtres privées » sont fermées. La permission s'accorde à l'installation, par le champ allowPrivateBrowsing de /moz/addon/install ; 46 lignes de clics XPath disparaissent. Un geckodriver qui ignore le champ installe sans la permission au lieu d'interrompre la session. Vérifié : sombre en fenêtre privée, blanc sans extension, repli servi. --- EN --- Firefox refuses navigation to about:addons from the content context, and the chrome context demands -remote-allow-system-access, which geckodriver rejects through capabilities: both routes to the « Run in Private Windows » checkbox are closed. The permission is now granted at install time, through the allowPrivateBrowsing field of /moz/addon/install; 46 lines of XPath clicking are gone. A geckodriver that ignores the field installs without the permission instead of killing the session. Checked: dark in a private window, white with no addon, fallback hit. Assisted-by: Claude Opus 5 --- script/selenium/selenium_lib.py | 105 ++++++++++++++------------------ 1 file changed, 47 insertions(+), 58 deletions(-) diff --git a/script/selenium/selenium_lib.py b/script/selenium/selenium_lib.py index ca71ab0..3b520c9 100644 --- a/script/selenium/selenium_lib.py +++ b/script/selenium/selenium_lib.py @@ -2,6 +2,7 @@ # © 2021-2026 TechnoLibre (http://www.technolibre.ca) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import base64 import datetime import getpass import json @@ -22,6 +23,7 @@ from selenium import webdriver from selenium.common.exceptions import ( ElementClickInterceptedException, TimeoutException, + WebDriverException, ) from selenium.webdriver import ActionChains from selenium.webdriver.common.actions.wheel_input import ScrollOrigin @@ -135,6 +137,44 @@ class SeleniumLib(object): ) self.driver.save_screenshot(file_path) + def _install_addon(self, relative_path, allow_private_browsing): + """Installe une extension, avec l'accès aux fenêtres privées si demandé. + + Prend un chemin relatif à la racine du dépôt, rend l'identifiant de + l'extension installée. + + La permission « fenêtres privées » s'accorde À L'INSTALLATION, par le + champ allowPrivateBrowsing de /moz/addon/install. Elle ne s'accorde + plus autrement : Firefox refuse la navigation vers about:addons depuis + le contexte contenu, et le contexte chrome exige + -remote-allow-system-access, que geckodriver refuse via les + capabilities — la case à cocher de l'interface est hors d'atteinte. + install_addon() de selenium n'expose pas ce champ, d'où la commande + montée à la main. + """ + path = os.path.join(new_path, relative_path) + with open(path, "rb") as addon_file: + addon = base64.b64encode(addon_file.read()).decode("UTF-8") + try: + return self.driver.execute( + "INSTALL_ADDON", + { + "addon": addon, + "temporary": True, + "allowPrivateBrowsing": allow_private_browsing, + }, + )["value"] + except WebDriverException: + # Un geckodriver qui ne connaît pas le champ rejette la requête + # entière. L'extension s'installe alors sans la permission : le + # mode sombre reste inactif en fenêtre privée, ce qui vaut mieux + # qu'une session interrompue au démarrage. + _logger.warning( + "geckodriver refuses allowPrivateBrowsing, installing" + f" {relative_path} without private window access" + ) + return self.driver.install_addon(path, temporary=True) + def configure(self, ignore_open_web=False): # Configuration pour lancer Firefox en mode de navigation privée firefox_options = webdriver.FirefoxOptions() @@ -372,67 +412,16 @@ class SeleniumLib(object): # TODO do a script to check if it's the last version if self.config.use_firefox_driver and not self.config.use_network: if not self.config.no_dark_mode: - self.driver.install_addon( - os.path.join( - new_path, "script/selenium/darkreader-firefox.xpi" - ), - temporary=True, + # Les extensions reçoivent l'accès aux fenêtres privées dès + # l'installation, seul moyen restant de le leur accorder. + allow_private = not self.config.not_private_mode + self._install_addon( + "script/selenium/darkreader-firefox.xpi", allow_private ) - self.driver.install_addon( - os.path.join( - new_path, "script/selenium/odoo_debug-4.0.xpi" - ), - temporary=True, + self._install_addon( + "script/selenium/odoo_debug-4.0.xpi", allow_private ) - if ( - not self.config.not_private_mode - and not self.config.no_dark_mode - ): - # self.driver.set_context("chrome") - self.driver.get("about:addons") - # Enable Dark Reader into incognito - # Click Extensions - self.click('//button[@viewid="addons://list/extension"]') - self.click( - "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/div/div/div/button", - ) - self.click( - "/html/body/div/div[2]/div/addon-list/section[1]/addon-card/div/addon-options/panel-list/panel-item[5]", - ) - # Enable Run in Private Windows - try: - self.click( - "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[6]/div/label[1]/input", - ) - except Exception: - # For old version before Firefox 138 - self.click( - "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", - ) - - # Enable Odoo_debug into incognito - # Back - self.click( - "/html/body/div/div[2]/addon-page-header/div/div[2]/button" - ) - self.click( - "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/div/div/div/button", - ) - self.click( - "/html/body/div/div[2]/div/addon-list/section[1]/addon-card[2]/div/addon-options/panel-list/panel-item[5]", - ) - # Enable Run in Private Windows - try: - self.click( - "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[6]/div/label[1]/input", - ) - except Exception: - # For old version before Firefox 138 - self.click( - "/html/body/div/div[2]/div/addon-card/div/addon-details/named-deck/section/div[5]/div/label[1]/input", - ) - # Ouvrez la page web if not ignore_open_web: self.driver.get(f"{self.config.url}/web")