Deux options sont souvent utilisées dans les exemples d’automatisation d’extensions :
--disable-extensions-except=/path/to/ext --load-extension=/path/to/ext
Les anciens guides font pointer Puppeteer ou Playwright vers une version de Chrome déjà installée, puis ajoutent ces deux arguments.
Dans la version standard de Google Chrome 150 testée ici, le navigateur a bien démarré depuis la ligne de commande, mais le service d’extensions a ignoré ces deux options. L’automatisation s’est connectée normalement et l’extension était absente ; le symptôme observé plus tard a été un timeout sur un sélecteur dans ce harness.
Chrome a bien consigné le refus sur une seule ligne, mais seulement après activation de la journalisation sur stderr.
Le point clé vient de la comparaison entre builds du navigateur. Les deux sections suivantes sont clairement des notes de harness : l’une traite des téléchargements déclenchés par l’extension, l’autre de ce chemin d’installation en ligne de commande sur des fixtures file://. Thunderbit est lui-même une extension Chrome, donc ce sujet nous concerne directement ; Thunderbit n’était toutefois pas la cible des tests.
La ligne de log
Ajoutez --enable-logging=stderr et lancez Chrome standard avec ces options :
Référence officielle : code source du service d’extensions Chromium.
WARNING:chrome/browser/extensions/extension_service.cc:442]
--disable-extensions-except is not allowed in Google Chrome, ignoring.
Si vous passez seulement --load-extension, vous obtenez son propre avertissement, à une autre ligne du même fichier :
WARNING:chrome/browser/extensions/extension_service.cc:420]
--load-extension is not allowed in Google Chrome, ignoring.
Chrome for Testing, avec les mêmes arguments, n’affiche rien.
« Not allowed in Google Chrome. » Cet avertissement fait de la règle propre aux builds brandés l’interprétation la plus probable du refus observé. Il montre que ce build standard de Google Chrome 150 a ignoré l’option et indique l’emplacement du code source. En revanche, il ne prouve pas à lui seul que la version n’a aucune importance, ni ne révèle le discriminant d’implémentation.
Tout ce qui suit sert à confirmer et à mesurer les conséquences.
Confirmation comportementale
Une extension MV3 minimale, construite pour ce test plutôt que téléchargée, utilise un content script, une popup, un aller-retour de message, une extraction du DOM et une exportation via chrome.downloads sur un fixture local de trois produits. Le harness enregistre six vérifications numérotées : service worker enregistré ; le marqueur d’extension contient un ID ; le marqueur du content script correspond ; le bouton de la popup est visible ; la popup renvoie trois lignes ; le CSV capturé contient la ligne attendue. Une assertion de cohérence séparée compare les signaux indépendants du service worker et du marqueur de page.
Trois builds ont utilisé les mêmes options explicites d’extension, le même répertoire d’extension, le même fixture HTTP, le mode contextuel persistant en affichage réel et un profil neuf pour chaque branche. La branche standard passait par channel: 'chrome' ; les deux branches validées utilisaient des chemins d’exécution explicites. Les chaînes de version ont été relues via CDP.
| Build | Version indiquée par le navigateur | Service worker de l’extension | Content script injecté | Exécution complète des six vérifications |
|---|---|---|---|---|
| Chrome for Testing | Chrome/149.0.7827.55 | ✅ | ✅ | RÉUSSITE 6/6 |
| Google Chrome standard | Chrome/150.0.7871.187 | ❌ jamais enregistré | ❌ | ÉCHEC à l’étape 0 |
| Chrome for Testing | Chrome/151.0.7922.10 | ✅ | ✅ | RÉUSSITE 6/6 |
Résumés bruts : Chrome for Testing 149, Chrome standard 150, Chrome for Testing 151 et avertissements stderr.
Chrome for Testing 149 est inclus volontairement : il est plus ancien que le build standard auquel il est comparé. Si la capacité avait disparu à cause d’un saut de version, un build plus ancien devrait se trouver du côté fonctionnel de la ligne et le build en échec devrait être le plus récent. Or le build en échec se situe entre deux builds fonctionnels, ce qui exclut une simple progression linéaire par version.
Ce tableau, à lui seul, ne prouve pas que le blocage dépend du build, et il vaut la peine d’être précis sur ce point. La seule cellule en échec est à la fois la seule cellule standard et la seule cellule 150 — build et version restent donc parfaitement confondus dans ce design. Trois branches permettent d’exclure une suppression monotone ; elles ne peuvent pas exclure « régressé en 150, rétabli en 151 ». Pour démontrer cela à partir du comportement seul, il faudrait une cellule que cette machine ne peut pas produire : Chrome for Testing 150, ou un build brandé à une autre version.
L’avertissement renforce l’interprétation « build brandé » parce qu’il nomme explicitement Google Chrome, tandis que le triptyque de résultats ne permet d’exclure qu’une suppression monotone simple. Une comparaison inter-build à version identique, ou une citation du code source/configuration, serait encore nécessaire pour prouver un mécanisme indépendant de la version.
Les artefacts par build liés plus haut montrent le résumé de chaque branche ; cet article ne publie pas de matrice exécution par exécution pour le nombre de lancements supplémentaire, donc ce décompte n’est pas utilisé comme preuve indépendante.
Un seul signal ne suffit pas pour dire « il ne s’est pas chargé »
La première version de ce test décidait si l’extension était chargée en cherchant un marqueur écrit dans la page par le content script — et c’est aussi ainsi qu’elle décidait si ce content script avait tourné. Une seule lecture, utilisée comme deux mesures. Si le marqueur manque, on ne peut pas savoir si « l’extension ne s’est jamais chargée » ou si « elle s’est chargée mais son content script ne s’est pas injecté », et les correctifs à appliquer sont totalement différents.
Les extensions MV3 exécutent un service worker en arrière-plan, et Playwright expose directement les service workers. C’est un signal indépendant : il ne touche jamais la page, donc il ne peut pas être confondu avec une injection. Le test actuel lit les deux et vérifie qu’ils concordent.
Dans les trois exécutions, ils concordent. Sur Chrome standard, aucun service worker n’a jamais été enregistré — la forme forte de l’affirmation. Sur les deux builds Chrome for Testing, le worker a démarré à chrome-extension://<id>/background.js avant même que la page ne soit ouverte.
Utilisez Chrome for Testing, que vous avez peut-être déjà
Les deux branches validées utilisaient des exécutables Chrome for Testing explicites, avec les versions 149.0.7827.55 et 151.0.7922.10. Orientez executablePath de Playwright vers l’exécutable figé plutôt que de résoudre Chrome standard via channel: 'chrome'. npx playwright install chromium installe un build Chromium géré par Playwright ; cela peut aussi servir pour l’automatisation, mais ce n’est pas le même label de distribution que les deux branches Chrome for Testing et cela ne constituait pas une quatrième branche dans cette comparaison.
Référence officielle : annonce Chrome for Testing.
Lecture associée : audit des autorisations d’extension Chrome.
Il y a aussi un avantage durable au-delà de ce bug précis. Chrome standard se met à jour tout seul en arrière-plan, donc une suite qui passe aujourd’hui peut échouer mardi pour des raisons qu’aucun commit n’explique. Chrome for Testing est figé. Pour tout résultat censé garder du sens dans trois mois, cela compte plus que la commodité.
Note de harness : capturer l’export de l’extension
Pour une extension de scraping, l’export est l’objectif principal — c’est là que des champs disparaissent, que des encodages se dégradent et que des données imbriquées se retrouvent aplaties à tort. Deux choses à son sujet ne sont pas documentées, et elles posent toutes les deux problème.
| Ce que l’exécution d’export a enregistré | Valeur |
|---|---|
playwright_download_event_fired | false |
suggestedFilename | null |
| Nom de fichier demandé par l’extension | probe-export.csv |
| Nom de fichier réellement déposé dans le répertoire | download.csv |
| Contenu du fichier | l’en-tête et les trois lignes sont conservés intacts |
Dans ce harness MV3 / Playwright 1.56.0, chrome.downloads n’a pas déclenché l’événement de téléchargement de Playwright. Lorsque l’extension exportait un CSV via l’API, waitForEvent('download') ne se résolvait pas. Le harness a récupéré le fichier en ouvrant une session CDP, en définissant explicitement le comportement de téléchargement, puis en lisant le répertoire de sortie :
const cdp = await ctx.newCDPSession(page);
await cdp.send('Browser.setDownloadBehavior', {
behavior: 'allow', downloadPath: DIR, eventsEnabled: true,
});
Dans le même harness, ce chemin de capture via CDP n’a pas conservé le nom de fichier demandé. L’en-tête et les trois lignes sont restés intacts, mais probe-export.csv a été enregistré sous download.csv. Il s’agit d’un comportement observé pour les builds de navigateur et la configuration testés, pas d’un invariant documenté pour tous les téléchargements d’extensions. Vérifie le contenu séparément du nom de fichier.
Note de harness : comportement file:// pour ce chemin d’installation
En vérifiant ce point, une affirmation très répandue s’est révélée fausse, et elle figurait même dans une version antérieure de cet article : l’idée que les content scripts ne s’exécutent pas sur des pages file:// parce que les extensions n’ont pas l’accès aux fichiers par défaut.
Mesuré sur les deux builds Chrome for Testing, en chargeant l’extension depuis la ligne de commande puis en ouvrant directement file:///…/fixture/index.html : le content script s’injecte normalement. Marqueur présent, ID d’extension correct, sur les deux builds. Les extensions décompressées chargées en ligne de commande obtiennent l’accès aux fichiers ; l’option « autoriser l’accès aux fichiers » dont on se souvient s’applique à un autre chemin d’installation.
Servir les fixtures via HTTP reste préférable par défaut, parce qu’une page file:// ne ressemble en rien à une vraie cible. Mais c’est un argument de réalisme, pas une exigence technique, et le mécanisme généralement invoqué à ce sujet est faux.
Ce que cela ne démontre pas
- Le mécanisme du blocage n’est connu qu’à travers la chaîne d’avertissement. Chrome dit que ces options ne sont pas autorisées dans ce build et nomme le fichier source. En revanche, rien dans ce test n’a permis de savoir si cela relève d’une configuration de build, d’un mécanisme de politique, ou d’autre chose.
- Cela ne repose que sur une seule machine. macOS sur arm64, une version patch standard, deux builds Chrome for Testing. Chrome évolue assez vite pour qu’il faille revérifier plutôt que citer comme vérité figée.
- Cela ne concerne que le chemin
--load-extension. Les installations de.crxempaquetés, le chargement en mode développeur et les politiques d’autorisation d’entreprise n’ont pas été testés. Rien ici ne permet d’affirmer que « Chrome standard ne peut pas exécuter d’extensions ». - L’extension est un stub conçu pour ce test. Elle reproduit les mécanismes qu’utilise toute extension de scraping, mais une vraie extension est plus complexe et peut échouer d’autres façons qu’un stub ne peut pas montrer.
Ce que j’ai mal compris en arrivant ici
Cela mérite d’être dit clairement, parce que ces deux erreurs sont exactement le genre qui survit à la relecture quand le résultat final semble correct.
La première version de ce texte disait que l’échec était silencieux, qu’aucune ligne de log n’existait, et que le mécanisme était impossible à connaître — que toute personne prétendant le connaître spéculait. Le mécanisme se trouvait à une option près, et Chrome l’affichait depuis le début au niveau WARNING. « Je n’ai pas trouvé » avait été reformulé en « on ne peut pas le trouver ».
La seconde était l’affirmation sur file:// ci-dessus : reprise de notes et rédigée avec un mécanisme présenté comme certain avant même d’avoir été testé. Une seule exécution l’a invalidée.
Le schéma est le même dans les deux cas. Une affirmation plausible que personne ne contesterait, poursuivie simplement parce que la vérification semblait inutile. La solution n’est pas davantage de prudence dans le style, mais une règle sur ce qui peut être affirmé : une assertion qui n’est pas traçable à une exécution ne doit pas être publiée.
Commandes utilisées dans le harness local
Le probe versionné, le stub d’extension, le fixture et les résumés bruts sont liés ici, mais il ne s’agit pas encore d’un bundle de reproduction public autonome. Les sources de téléchargement exactes et les sommes de contrôle des Chrome for Testing 149 et 151 ne sont pas consignées dans l’article, et les chemins d’exécutable ci-dessous étaient des entrées locales. Publie ces sources de navigateur ainsi qu’un commit stable du dépôt avant de présenter la comparaison complète comme indépendamment reproductible.
Lecture associée : revue Playwright.
cd harness
npm install playwright@1.56.0
npx playwright install chromium # Chromium géré par Playwright ; pas les deux branches CfT ci-dessous
(cd fixture && python3 -m http.server 8731 &)
SP=$(pwd) OUT=cft-149.json LABEL=cft-149 EXE="<Chrome for Testing 149>" node probe_v2.mjs
SP=$(pwd) OUT=stock-150.json LABEL=stock-150 CHANNEL=chrome node probe_v2.mjs
SP=$(pwd) OUT=cft-151.json LABEL=cft-151 EXE="<Chrome for Testing 151>" node probe_v2.mjs
Le même script et les mêmes arguments explicites d’extension ont été utilisés pour les trois branches, mais la résolution de l’exécutable différait : CHANNEL=chrome pour Chrome standard et EXE pour les deux binaires Chrome for Testing. La version indiquée dans chaque fichier de sortie est lue depuis le navigateur et non supposée à partir du label.
Pour la ligne d’avertissement, aucun harness n’est nécessaire :
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--user-data-dir=/tmp/p --enable-logging=stderr \
--load-extension=/path/to/ext about:blank 2>&1 | grep "not allowed"
À la date du 2026-07-28.
Essayez Thunderbit pour l’extraction de données web
Décision et réserves
Dans la matrice testée, Google Chrome standard 150 a refusé --disable-extensions-except et --load-extension, tandis que Chrome for Testing 149 et 151 ont chargé l’extension. Chrome a journalisé le refus au niveau WARNING : « --load-extension is not allowed in Google Chrome, ignoring. » La formulation soutient une interprétation liée au build brandé, et l’encadrement par deux versions écarte une simple suppression monotone, mais build et version restent confondus sans branche inter-build à version identique ni preuve tirée du code source/de la configuration.
Pour un harness d’extension figé, utilise un exécutable navigateur versionné explicitement et vérifie le service worker ainsi que le marqueur du content script. Dans ce setup Playwright 1.56.0, l’export de l’extension nécessitait une capture CDP du répertoire et arrivait sous un nom de fichier différent. Le stub chargé en ligne de commande s’injectait aussi sur le fixture file:// testé ; les autres chemins d’installation n’ont pas été testés.
Essayez Thunderbit pour l’extraction de données web Get Started Free
FAQ
--load-extension a-t-il été supprimé de Chrome ?
Non. Chrome for Testing 149.0.7827.55 et 151.0.7922.10 ont tous deux chargé le stub MV3 décompressé via cette option et validé les six vérifications. Google Chrome standard 150.0.7871.187 l’a refusée et a consigné « --load-extension is not allowed in Google Chrome, ignoring ». Cela soutient, sans le prouver, une barrière liée au build brandé : le comportement exclut une simple suppression monotone, tandis qu’une régression propre à Chrome 150, corrigée en 151, reste compatible avec ce design à trois branches.
Pourquoi ne vois-je aucune erreur, et comment distinguer « jamais chargé » de « chargé puis échec silencieux » ?
L’avertissement n’est pas affiché au niveau de verbosité par défaut. Lance Chrome avec --enable-logging=stderr et il apparaît immédiatement ; sans cela, Chrome démarre normalement mais son service d’extensions ignore les options, et le premier symptôme dans le harness est un timeout sur un sélecteur. Pour séparer « jamais chargé » d’un échec d’injection, utilise deux signaux indépendants : le service worker d’arrière-plan MV3 et un marqueur de content script dans le DOM cible. Sur la branche Chrome standard testée, aucun des deux n’apparaissait ; sur les deux branches Chrome for Testing, les deux étaient présents.
Que dois-je utiliser à la place pour automatiser des extensions ?
Les branches qui réussissent utilisaient des exécutables Chrome for Testing figés via executablePath. Le Chromium géré par Playwright est une autre option possible pour l’automatisation, mais il ne faisait pas partie des branches testées ici et ne doit pas être présenté comme la même distribution sans vérifier l’exécutable réellement résolu.
Pourquoi waitForEvent('download') ne se résout-il jamais quand l’extension exporte un fichier ?
Dans ce setup MV3 / Playwright 1.56.0, l’événement ne se résolvait pas et aucun nom de fichier suggéré n’était fourni. Le harness ouvrait une session CDP, appelait Browser.setDownloadBehavior avec un répertoire explicite, puis lisait le fichier sur disque. Dans les exécutions testées, les octets du CSV étaient intacts mais probe-export.csv arrivait sous le nom download.csv ; les autres combinaisons d’extensions et de navigateurs n’ont pas été testées.
Qu’est-ce que cela ne dit pas — à propos des URL file://, et de Chrome standard en général ?
Deux limites, dans des sens opposés. Les content scripts fonctionnent sur les URL file:// pour les extensions chargées depuis la ligne de commande — testé sur les deux builds Chrome for Testing, avec injection normale du script et ID d’extension correctement signalé, donc l’affirmation courante selon laquelle ils ne fonctionnent pas (parce que les extensions n’auraient pas l’accès aux fichiers par défaut) est fausse pour ce chemin d’installation. Servir les fixtures via HTTP reste une bonne pratique parce que cela ressemble davantage à une cible réelle, et non parce que file:// bloquerait l’injection. Dans l’autre sens : rien ici n’affirme que Chrome standard ne peut pas exécuter d’extensions. Seul le chemin en ligne de commande --load-extension a été mesuré. L’installation de .crx empaquetés, le chargement en mode développeur et les politiques d’autorisation d’entreprise n’ont pas été testés, et aucune conclusion n’est tirée à leur sujet. Le périmètre est la paire d’options que les tutoriels d’automatisation te disent d’utiliser.


