56 votes

Détecter l'exécution de Chrome en mode sans tête à partir de JavaScript

Avec la sortie de Chrome 59, le mode " sans tête " est maintenant disponible dans les versions stables pour Linux et macOS (et bientôt aussi pour Windows avec Chrome 60). Cela nous permet d'exécuter une version complète de Chrome sans interface utilisateur visible, ce qui est très utile pour les tests automatisés. Voici quelques exemples.

chrome --headless --disable-gpu --dump-dom https://stackoverflow.com/

Dans mon programme de test JavaScript, j'aime enregistrer autant d'informations que possible sur le navigateur utilisé, afin d'aider à isoler les problèmes. Par exemple, j'enregistre plusieurs des propriétés de navigator y compris les plugins de navigateur actuels :

JSON.stringify(Array.from(navigator.plugins).map(p => p.name))

["Chrome PDF Viewer","Widevine Content Decryption Module","Shockwave Flash","Native Client","Chrome PDF Viewer"]

Je crois savoir que Chrome debe se comportent de manière identique en mode sans tête, mais j'ai suffisamment d'expérience pour être sceptique à l'égard d'une nouvelle fonctionnalité qui pourrait modifier de manière significative le pipeline de rendu.

Pour l'instant, je vais effectuer des tests dans les deux modes. J'aimerais que le gestionnaire de tests enregistre si le mode sans tête est utilisé. Je pourrais transmettre cette information dans la configuration des tests, mais je préfère avoir une solution purement JavaScript que je peux intégrer dans l'exécuteur de tests lui-même. Cependant, je n'ai pas pu trouver d'interface de navigateur qui indique si le mode sans tête est actif.

Existe-t-il un moyen de détecter si Chrome fonctionne en mode sans tête à partir de JavaScript ?

34voto

Josh Lee Points 53741

El agent utilisateur chaîne de caractères comprend HeadlessChrome au lieu de Chrome . C'est probablement le signal que vous êtes censé rechercher, vous pouvez donc l'utiliser :

/\bHeadlessChrome\//.test(navigator.userAgent)

D'autres signaux intéressants incluent :

  • On dirait que window.chrome est indéfini lorsqu'il est sans tête.
  • [innerWidth, innerHeight] es [800, 600] (codé en dur dans headless_browser.cc ), tandis que [outerWidth, outerHeight] es [0, 0] (ce qui ne devrait normalement pas arriver).

2 votes

La chaîne de l'agent utilisateur peut être remplacée par une option de chrome.

18voto

Justas Points 2539

Vous pouvez vérifier navigator.webdriver qui est :

El webdriver propriété en lecture seule de la navigator L'interface indique si l'agent utilisateur est contrôlé par l'automatisation.

...

El navigator.webdriver est vrai lorsque l'on est dans :

Chrome Le site --enable-automation ou le --headless est utilisé.
Firefox Le site marionette.enabled préférence ou --marionette est passé.

La recommandation WebDriver du W3C le décrit comme suit :

navigator.webdriver Définit un moyen standard pour les agents utilisateurs coopérants d'informer le document qu'il est contrôlé par WebDriver, par exemple pour que des chemins de code alternatifs puissent être déclenchés pendant l'automatisation.

9voto

Hugues M. Points 11750

Lis juste cet article par Antoine Vastel qui fournit quelques moyens :

  • tester l'agent utilisateur avec /HeadlessChrome/.test(window.navigator.userAgent) mais il est facile de l'usurper
  • tester les plugins avec navigator.plugins.length == 0
  • les langues d'essai avec navigator.languages == ""
  • test des informations sur le vendeur et le moteur de rendu WebGL (voir l'article pour des détails intéressants)
  • test des fonctionnalités supportées telles que détectées par Modernizr : il semble que les "hairlines" ne soient pas supportées (hairlines hidpi/retina, qui sont des bordures CSS de moins de 1px de largeur, pour être physiquement 1px sur les écrans hidpi). Le test est !Modernizr["hairline"] .
  • test de la taille de l'espace réservé à l'image manquante. Insérez une image dont l'URL n'est pas valide, et testez la présence de image.width == 0 && image.height == 0 sur image.onerror (ils ont trouvé que celle-ci était la plus robuste).

Je ne peux pas parler des motivations de Google ( Headless Chrome ne sert-il qu'à faciliter les tests d'applications web ? Hmmm... ), mais cela pourrait être vu comme une liste de bugs qui pourraient être corrigés un jour, donc on peut se demander combien de temps ces tests fonctionneront :)

0 votes

Je viens de remarquer après avoir posté ce message que Josh Lee en a parlé dans un commentaire sous la question d'hier. J'ai lu le même article et me suis souvenu de cette question du mois dernier... J'espère avoir trouvé le bon équilibre entre la publicité pour l'auteur original et le fait de ne pas avoir plagié. Si c'est trop, je suis prêt à modifier, supprimer, etc. (merci Jeremy pour l'édition)

0 votes

Navigator.languages == "", ne semble pas être un test valide. La langue préférée de mon navigateur est "en-GB" alors que la langue préférée de mon système est "en-US". navigator.languages dans un navigateur chromium normal renvoie ["en-GB", "en-US", "en"], alors que dans un navigateur chromium sans tête renvoie [ 'en-US'].

5voto

Jeremy Banks Points 32470

La meilleure solution que j'ai trouvée jusqu'à présent est ce hack. Je ne l'utiliserais pas dans le code de production, mais peut-être dans les tests.

Le bloqueur de fenêtres pop-up de Chrome est généralement activé pour tous les sites Web, mais il est désactivé en mode sans affichage. Nous pouvons utiliser la capacité d'ouvrir des fenêtres pop-up comme un indicateur assez précis du mode sans tête. L'implémentation est simple : essayez de open(...) une fenêtre, et vérifions si nous obtenons null (indiquant qu'il a été bloqué) au lieu d'un Window objet. Si on en ouvre un, il faut le fermer le plus vite possible.

function canPopUp() {
  var w = open("");
  if (w !== null) {
    w.close();
    return true;
  } else {
    return false;
  }
}

var isHeadless = canPopUp;

Pour un exemple rapide, vous pouvez essayer ce qui suit avec et sans l'option --headless drapeau :

chrome --headless --disable-gpu --dump-dom 'data:text/html,<!doctype html><body><script>document.body.innerHTML = `headless: ${open("") !== null}`;</script>'

4voto

FieryCat Points 1303

navigator.plugins doit contenir un tableau de plugins présents dans le navigateur (comme Flash, ActiveX ou les applets Java). Pour headless navigateur il sera nullable.

Dans le cadre d'un contrôle de sécurité, il peut être utilisé alert pour les sans tête, il sera ignoré :

var start = Date.now();
alert('Press OK');
var elapse = Date.now() - start;
if (elapse < 15) {
    console.log("headless environment detected");
}

Plusieurs techniques de détection des navigateurs sans tête sont présentées dans l'exposé de l'OWASP AppSecUSA 2014. Cache-cache du navigateur sans tête ( vidéo , diapositives ) par Sergey Shekyan et Bei Zhang.

0 votes

Je confirme que je vois navigator.plugins.length === 0 en Chrome sans tête. Commande de test : chrome --headless --disable-gpu --dump-dom 'data:text/html,<!doctype html><body><script>document.body.innerHTML = `plugins: ${navigator.plugins.length} ${Array.from(navigator.plugins).map(p => p.name)}`;</script>'

2 votes

Mais j'ai eu un "faux-positif" pour vous. J'utilise Chrome sur mon téléphone Android et il renvoie navigator.plugins.length === 0 . Donc, mieux vaut ne pas l'utiliser.

1 votes

Oui, le test sur chrome sous linux retourne également des plugings : 0. Ce n'est donc pas un test fiable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X