Mit der Veröffentlichung von Chrome 59 ist der "Headless" -Modus jetzt verfügbar in stabilen Builds für Linux und MacOS (und bald auch Windows mit Chrome 60). Auf diese Weise können wir eine Vollversion von Chrome ohne sichtbare Benutzeroberfläche ausführen. Dies ist eine hervorragende Funktion für automatisierte Tests. Hier sind Beispiele.
chrome --headless --disable-gpu --dump-dom https://stackoverflow.com/
In meinem JavaScript-Testläufer möchte ich so viele Informationen wie möglich über den verwendeten Browser aufzeichnen, um Probleme einzugrenzen. Zum Beispiel zeichne ich viele der Eigenschaften von navigator
auf, einschließlich der aktuellen Browser-Plugins:
JSON.stringify(Array.from(navigator.plugins).map(p => p.name))
["Chrome PDF Viewer","Widevine Content Decryption Module","Shockwave Flash","Native Client","Chrome PDF Viewer"]
Meines Wissens nach sollte sich Chrome im kopflosen Modus identisch verhalten , aber ich habe genug Erfahrung, um skeptisch gegenüber einer neuen Funktion zu sein, die sich möglicherweise erheblich ändert die Rendering-Pipeline.
Im Moment werde ich Tests in beiden Modi durchführen. Ich möchte dem Testläufer mitteilen, ob der Headless-Modus verwendet wird. Ich könnte diese Informationen in den Testkonfigurationen weitergeben, hätte aber lieber eine reine JavaScript-Lösung, die ich in den Testläufer selbst einbauen kann. Ich konnte jedoch keine Browseroberfläche finden, die angibt, ob der Headless-Modus aktiv ist.
Gibt es eine Möglichkeit, über JavaScript zu erkennen, ob Chrome im Headless-Modus ausgeführt wird?
Du kannst nachschauen navigator.webdriver
Eigenschaft, die ist:
Die schreibgeschützte Eigenschaft
webdriver
der Schnittstellenavigator
gibt an, ob der Benutzeragent durch die Automatisierung gesteuert wird....
Das
navigator.webdriver
Eigenschaft ist wahr, wenn in:Chrome Die
--enable-automation
oder der--headless
flag wird verwendet.
Firefox Diemarionette.enabled
Präferenz oder--marionette
Flagge wird übergeben.
Die W3C WebDriver-Empfehlung beschreibt sie wie folgt:
navigator.webdriver
Definiert eine Standardmethode für kooperierende Benutzeragenten, um das Dokument darüber zu informieren, dass es von WebDriver gesteuert wird, z. B. damit während der Automatisierung alternative Codepfade ausgelöst werden können.
Die Zeichenfolge des Benutzeragenten enthält HeadlessChrome
anstelle von Chrome
. Dies ist wahrscheinlich das Signal, nach dem Sie suchen möchten. Sie können also Folgendes verwenden:
/\bHeadlessChrome\//.test(navigator.userAgent)
Andere interessante Signale sind:
window.chrome
ist im kopflosen Zustand undefiniert.[innerWidth, innerHeight]
ist [800, 600]
(fest codiert in headless_browser.cc
), während [outerWidth, outerHeight]
ist [0, 0]
(was normalerweise nicht passieren sollte).Lesen Sie einfach dieser Artikel von Antoine Vastel , der einige Möglichkeiten bietet:
/HeadlessChrome/.test(window.navigator.userAgent)
, dies kann jedoch leicht gefälscht werdennavigator.plugins.length == 0
navigator.languages == ""
!Modernizr["hairline"]
.image.width == 0 && image.height == 0
im image.onerror
(sie fanden, dass dies das robusteste war).Ich kann nicht für Googles Motivationen sprechen ( ist kopflos Chrome nur zum Ermöglichen von Tests von Webanwendungen? Hmmm ... ), aber dies könnte als eine Liste von Fehlern angesehen werden, die eines Tages behoben werden könnten, daher muss man sich fragen, wie lange diese Tests funktionieren werden :)
Die beste Lösung, die ich bisher habe, ist dieser Hack. Ich würde es nicht in Prod-Code verwenden, aber möglicherweise beim Testen.
Der Popup-Blocker von Chrome ist normalerweise für alle Websites aktiviert, im Headless-Modus jedoch deaktiviert. Wir können die Möglichkeit nutzen, Popups als ziemlich genauen Proxy für den Headless-Modus zu öffnen. Die Implementierung ist einfach: Versuchen Sie, ein Fenster zu open(...)
und prüfen Sie, ob wir null
(was darauf hinweist, dass es blockiert wurde) anstelle eines Window
-Objekts erhalten. Wenn wir einen öffnen, schließen Sie ihn so schnell wie möglich.
function canPopUp() {
var w = open("");
if (w !== null) {
w.close();
return true;
} else {
return false;
}
}
var isHeadless = canPopUp;
Für ein kurzes Beispiel können Sie Folgendes mit und ohne das Flag --headless
Versuchen:
chrome --headless --disable-gpu --dump-dom 'data:text/html,<!doctype html><body><script>document.body.innerHTML = `headless: ${open("") !== null}`;</script>'
navigator.plugins
sollte eine Reihe von Plugins enthalten, die im Browser vorhanden sind (z. B. Flash, ActiveX oder Java Applets). Für den Browser headless
ist dies nullwertfähig.
Im Rahmen der Sicherheitsprüfung kann es alert
verwendet werden, bei Headless wird es ignoriert:
var start = Date.now();
alert('Press OK');
var elapse = Date.now() - start;
if (elapse < 15) {
console.log("headless environment detected");
}
In der Diskussion zu OWASP AppSecUSA 2014 werden verschiedene Techniken zum Erkennen von Headless-Browsern erläutert. Headless Browser Hide & Seek ( video , Folien ) von Sergey Shekyan und Bei Zhang.