Intervenir contre document.write()

Avez-vous récemment vu un avertissement semblable au suivant dans la Play Console de Chrome, et vous êtes-vous demandé ce que c'était ?

(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'une des plus grandes capacités du Web, car elle nous permet de l'intégrer facilement à des services créés par des tiers pour concevoir de nouveaux produits de qualité. L'un des inconvénients de la composition 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 cela puisse paraître, cela 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 l'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 premier affichage de la page.

Pour les utilisateurs disposant de connexions lentes, telles 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 empêcher le chargement des pages ou entraîner un temps tellement long que l'utilisateur abandonne. En nous appuyant sur l'instrumentation de 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 essai en conditions réelles de 28 jours auprès de 1% des utilisateurs de la version stable de Chrome, avec l'accès uniquement aux utilisateurs connectés à une connexion 2G. Nous avons constaté que 7, 6% de tous les chargements de page en 2G comprenaient au moins un script bloquant les analyseurs intersites inséré via document.write() dans le document de premier niveau. Suite au blocage de ces scripts, nous avons constaté les améliorations suivantes:

  • 10% de chargements de page en plus atteignant First Contentful Paint (une confirmation visuelle pour l'utilisateur que la page se charge correctement), 25% de chargements de page en plus atteignant l'état entièrement analysé et 10% d'actualisations en moins suggérant une diminution de la frustration des utilisateurs.
  • Réduction de 21% du délai moyen (de plus d'une seconde plus rapide) jusqu'au First Contentful Paint.
  • Réduction de 38% du temps moyen nécessaire pour analyser une page, soit 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.

Avec ces données à l'esprit, à partir de la version 55, Chrome intervient pour le compte de tous les utilisateurs lorsque nous détectons ce schéma malveillant connu en modifiant le traitement de document.write() 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, ce changement pourra être étendu à d'autres utilisateurs dont la connexion est lente, comme la 3G ou le Wi-Fi bas.)
  2. Le document.write() se trouve dans un document de premier niveau. L'intervention ne s'applique pas aux scripts "document.write" dans des cadres iFrame, car ils ne bloquent pas l'affichage de la page principale.
  3. Le script dans document.write() bloque les analyseurs. Les scripts avec les attributs async ou defer continuent de s'exécuter.
  4. Le script n'est pas hébergé sur le même site. En d'autres termes, Chrome n'intervient 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 du cache n'entraînent pas de retard du réseau et continuent de s'exécuter.
  6. Il ne s'agit pas d'une demande d'actualisation de la page. Chrome n'intervient pas si l'utilisateur a déclenché une actualisation et exécute la page normalement.

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

Comment résoudre ce problème ?

La réponse simple consiste à ne pas injecter de scripts à l'aide de document.write(). Nous disposons d'un ensemble de services connus pour la compatibilité avec les chargeurs asynchrones que nous vous encourageons à vérifier régulièrement.

Si votre fournisseur ne figure pas dans la liste et qu'il prend en charge le chargement asynchrone des scripts, veuillez nous en informer. Nous mettrons à jour la page pour aider tous les utilisateurs.

Si votre fournisseur ne propose pas la possibilité de charger des scripts de manière asynchrone sur votre page, nous vous invitons à le contacter et à nous le faire savoir en quoi cela peut être affecté.

Si votre fournisseur vous fournit un extrait de code incluant le document.write(), vous pouvez 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().

Détecter les conséquences sur votre site

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

Détecter quand un utilisateur est en 2G

Pour comprendre l'impact potentiel de ce changement, vous devez d'abord déterminer combien de vos utilisateurs utiliseront la 2G. Vous pouvez détecter le type et le débit de réseau actuels de l'utilisateur à l'aide de l'API Network Information disponible dans Chrome, puis envoyer un avertissement à vos systèmes d'analyse 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étectez les avertissements dans les outils pour les développeurs Chrome

Depuis Chrome 53, les outils de développement émettent 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 ressemblera à ceci:

Avertissement d&#39;écriture de document

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

Vérifiez 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 peut ê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 demande 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 de Chrome 53. (La version bêta a eu lieu en juillet 2016. La version stable devrait être disponible pour tous les utilisateurs en septembre 2016.)

Nous interviendrons pour bloquer les scripts injectés pour les utilisateurs 2G à partir de Chrome 54, dont la version stable est estimée à mi-octobre 2016 pour tous les utilisateurs. Pour plus d'informations, consultez l'entrée Chrome Status (État de Chrome).

Au fil du temps, nous cherchons à intervenir lorsque la connexion d'un utilisateur est lente (c'est-à-dire lorsque la 3G ou le Wi-Fi sont lentes). Suivez cette entrée sur l'état de Chrome.

Vous voulez en savoir plus ?

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