[FIX] selenium : accorder le privé aux extensions sans about:addons

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
This commit is contained in:
Mathieu Benoit 2026-09-14 04:39:21 -04:00
parent c15d97f646
commit aff0d50972

View file

@ -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")