Intervenir contre document.write()

Avez-vous récemment vu un avertissement de ce type dans votre Developer Console dans Chrome et vous êtes-vous demandé de quoi il s'agissait ?

(index):34 A Parser-blocking, cross-origin script,
https://paul.kinlan.me/ad-inject.js, is invoked via document.write().
This may be blocked by the browser if the device has poor network connectivity.

La composabilité est l'un des grands pouvoirs du Web. Elle nous permet d'intégrer facilement des services conçus par des tiers pour concevoir de nouveaux produits de qualité. L'un des inconvénients de la composabilité est qu'elle implique une responsabilité partagée concernant l'expérience utilisateur. Si l'intégration n'est pas optimale, l'expérience utilisateur en sera affectée.

L'une des causes connues de mauvaises performances est l'utilisation de document.write() dans les pages, en particulier celles qui injectent des scripts. Aussi inoffensif que le suivant, il peut causer de réels problèmes aux utilisateurs.

document.write('<script src="https://example.com/ad-inject.js"></script>');

Avant de pouvoir afficher une page, le navigateur doit créer l'arborescence DOM en analysant le balisage HTML. Chaque fois que l'analyseur rencontre un script, il doit s'arrêter et l'exécuter avant de pouvoir continuer à analyser le code HTML. Si le script injecte un autre script de manière dynamique, l'analyseur est obligé d'attendre encore plus longtemps le téléchargement de la ressource, ce qui peut entraîner un ou plusieurs allers-retours sur le réseau et retarder le délai de premier affichage de la page.

Pour les utilisateurs disposant d'une connexion lente, telle que la 2G, les scripts externes injectés de manière dynamique via document.write() peuvent retarder l'affichage du contenu de la page principale pendant des dizaines de secondes, ou entraîner l'échec du chargement des pages ou la durée d'exécution que l'utilisateur ne fait qu'abandonner. Grâce à l'instrumentation dans Chrome, nous avons découvert que les pages comportant des scripts tiers insérés via document.write() sont généralement deux fois plus lentes à charger que les autres pages en 2G.

Nous avons collecté les données d'un test en conditions réelles de 28 jours sur 1% des utilisateurs de la version stable de Chrome, limité aux utilisateurs de connexions 2G. Nous avons constaté que 7,6% de tous les chargements de page en 2G incluaient au moins un script intersites bloquant les analyseurs, inséré via document.write() dans le document de premier niveau. En bloquant la charge de ces scripts, nous avons constaté les améliorations suivantes:

  • 10% de chargements de pages en plus jusqu'au First Contentful Paint (une confirmation visuelle de l'utilisateur que la page est en cours de chargement), 25% de chargements de pages supplémentaires atteignent l'état d'analyse complète et 10% d'actualisations en moins indiquant une diminution de la frustration de l'utilisateur.
  • Diminution de 21% du temps moyen (plus d'une seconde plus rapide) avant le First Contentful Paint
  • 38% de réduction du temps moyen nécessaire pour analyser une page, ce qui représente une amélioration de près de six secondes, ce qui réduit considérablement le temps nécessaire pour afficher les informations importantes pour l'utilisateur.

En gardant ces données à l'esprit, à partir de la version 55, Chrome interagit au nom de tous les utilisateurs lorsque nous détectons ce type de comportement malveillant connu en modifiant la façon dont document.write() est géré dans Chrome (voir État de Chrome). Plus précisément, Chrome n'exécute pas les éléments <script> injectés via document.write() lorsque toutes les conditions suivantes sont remplies:

  1. La connexion de l'utilisateur est lente, en particulier lorsqu'il est en 2G. À l'avenir, cette modification pourra être étendue à d'autres utilisateurs ayant une connexion lente, par exemple en cas de connexion 3G ou Wi-Fi lente.
  2. Le document.write() se trouve dans un document de premier niveau. L'intervention ne s'applique pas aux scripts document.writes dans les iFrames, car ils ne bloquent pas l'affichage de la page principale.
  3. Le script de document.write() bloque les analyseurs. Les scripts avec les attributs async ou defer continueront de s'exécuter.
  4. Le script n'est pas hébergé sur le même site. En d'autres termes, Chrome n'interviendra pas pour les scripts dont l'eTLD+1 correspond (par exemple, un script hébergé sur js.example.org et inséré sur www.example.org).
  5. Le script ne se trouve pas déjà dans le cache HTTP du navigateur. Les scripts présents dans le cache n'engendreront pas de retard réseau et continueront de s'exécuter.
  6. La demande de page n'est pas une actualisation. Chrome n'intervient pas si l'utilisateur déclenche une actualisation et exécute la page normalement.

Les extraits tiers utilisent parfois document.write() pour charger des scripts. Heureusement, la plupart des fournisseurs tiers proposent des alternatives de chargement asynchrone, qui permettent de charger des scripts tiers sans bloquer l'affichage du reste du contenu sur la page.

Comment résoudre ce problème ?

Par exemple, n'injectez pas de scripts à l'aide de document.write(). Nous disposons d'un ensemble de services connus pour la compatibilité des chargeurs asynchrones, que nous vous encourageons à vérifier.

Si votre fournisseur ne figure pas dans la liste et qu'il est compatible avec le chargement de script asynchrone, veuillez nous le signaler afin que nous puissions mettre à jour la page afin d'aider tous les utilisateurs.

Si votre fournisseur ne permet pas de charger des scripts de manière asynchrone sur votre page, nous vous invitons à le contacter et à nous expliquer les conséquences pour vous.

Si votre fournisseur vous fournit un extrait qui inclut document.write(), vous pouvez peut-être ajouter un attribut async à l'élément de script, ou ajouter les éléments de script avec des API DOM telles que document.appendChild() ou parentNode.insertBefore().

Comment détecter quand votre site est affecté ?

De nombreux critères déterminent si la restriction est appliquée. Comment savoir si vous êtes concerné ?

Détecter les connexions 2G d'un utilisateur

Pour comprendre l'impact potentiel de ce changement, vous devez d'abord déterminer combien de vos utilisateurs se serviront de la 2G. Vous pouvez détecter le type et la vitesse du réseau actuels de l'utilisateur à l'aide de l'API Network Information disponible dans Chrome, puis envoyer un avertissement à vos systèmes analytiques ou de métriques utilisateur réelles (RUM).

if(navigator.connection &&
    navigator.connection.type === 'cellular' &&
    navigator.connection.downlinkMax <= 0.115) {
    // Notify your service to indicate that you might be affected by this restriction.
}

Détecter les avertissements dans les outils pour les développeurs Chrome

Depuis Chrome 53, les outils de développement génèrent des avertissements pour les instructions document.write() problématiques. Plus précisément, si une requête document.write() répond aux critères 2 à 5 (Chrome ignore les critères de connexion lors de l'envoi de cet avertissement), l'avertissement se présente comme suit:

Avertissement d&#39;écriture de document.

C'est très bien de voir des avertissements dans les outils pour les développeurs Chrome, mais comment les détecter à grande échelle ? Vous pouvez vérifier les en-têtes HTTP envoyés à votre serveur lorsque l'intervention se produit.

Vérifier vos en-têtes HTTP sur la ressource de script

Lorsqu'un script inséré via document.write a été bloqué, Chrome envoie l'en-tête suivant à la ressource demandée:

Intervention: <https://shorturl/relevant/spec>;

Lorsqu'un script inséré via document.write est détecté et pourrait être bloqué dans différentes circonstances, Chrome peut envoyer:

Intervention: <https://shorturl/relevant/spec>; level="warning"

L'en-tête d'intervention sera envoyé dans le cadre de la requête GET pour le script (de manière asynchrone en cas d'intervention réelle).

Que nous réserve l'avenir ?

Le plan initial consiste à exécuter cette intervention lorsque nous détectons que les critères sont remplis. Nous avons commencé par afficher un simple avertissement dans la Play Console dans Chrome 53. (version bêta en juillet 2016. La version stable devrait être disponible pour tous les utilisateurs en septembre 2016.

Nous allons intervenir pour bloquer les scripts injectés pour les utilisateurs 2G à partir de Chrome 54, qui devrait être une version stable pour tous les utilisateurs à la mi-octobre 2016. Pour plus d'informations sur les mises à jour, consultez la section État de Chrome.

Au fil du temps, nous cherchons à intervenir lorsque la connexion est lente (3G ou Wi-Fi, par exemple). Suivez cette entrée "État de Chrome".

Vous voulez en savoir plus ?

Pour en savoir plus, consultez ces ressources supplémentaires: