Xterm.js: Crash du navigateur lié à l'addon d'ajustement renvoyant une géométrie avec des tailles "Infinity"

Créé le 26 avr. 2018  ·  26Commentaires  ·  Source: xtermjs/xterm.js

screenshot 2018-04-25 18 34 47

Semble être causé par des cas marginaux autour des xterms hors écran entrant / sortant à l'écran. Avec term.renderer.dimensions.actualCellWidth comme null , l'ajustement renvoie Infinity . Si vous autorisez xterm à essayer de se redimensionner à Infinity, il consomme toute la RAM disponible et plante le navigateur.

areaddofit help wanted typbug

Commentaire le plus utile

Je viens de remarquer l'exigence selon laquelle xterm exige que le parent soit visible lorsque open est appelé . Nous avons peut-être tort, alors j'essaierai d'y jeter un deuxième coup d'œil plus tard.

Tous les 26 commentaires

Voici un CodePen qui reproduit principalement : https://codepen.io/akanet/pen/zjBgXa

@Tyriar désolé de

Je pense que je rencontre le même problème.

J'ai ajouté ce qui suit à la fin de cette fonction

    console.log("fit proposed geometry", geometry);
    if (geometry.cols == Infinity || geometry.rows == Infinity) {
      return null;
    }

mais maintenant, l'ajustement ne fonctionne plus du tout. Je pense qu'il est cassé.

Je viens de mettre ça en haut :

var cellWidth = term.renderer.dimensions.actualCellWidth || 10;
var cellHeight = term.renderer.dimensions.actualCellHeight || 15;

et le réglage approprié vers le bas :

    var geometry = {
        cols: Math.floor(availableWidth / cellWidth),
        rows: Math.floor(availableHeight / cellHeight)
    };

Les choses commencent à fonctionner un peu mieux, mais de toute évidence, l'ajustement est mauvais.

Si vous mettez ceci en haut de l'ajustement :

    if (!cellWidth || !cellHeight)
      return null;

et n'ont pas de valeurs par défaut, il ne s'affiche jamais correctement, et donc les champs actualCell* du moteur

J'atténue actuellement en utilisant par défaut une valeur saine (mais fausse) dans le cas d'Infinity, et en réexécutant fit sur un délai d'attente. Ce n'est pas idéal.

Ouais, mais je ne comprends pas pourquoi il est nécessaire d'utiliser par défaut la mauvaise valeur... J'essaie juste de comprendre.

D'une manière ou d'une autre, si j'appelle terminal.resize(NaN, NaN) , cela finit par fonctionner. Mais si j'appelle autre chose, par exemple terminal.resize(10, 10) , cela ne fonctionne jamais.

        //this.terminal.fit();
        this.terminal.resize(NaN, NaN);

semble tellement faux.

Cela ne fonctionne littéralement pas du tout si je ne l'ai pas. Le seul autre ajustement temporel est appelé lorsque l'élément parent change de taille.

D'une manière ou d'une autre, le moteur de rendu ne calcule jamais de valeurs à moins que vous ne le fassiez.

C'est ce que j'ai fini avec, qui fonctionne. Gah.

Terminal.prototype.proposeGeometry = function() {
        if (!this.element.parentElement) {
                return null;
        }

        var cellWidth = this.renderer.dimensions.actualCellWidth || 9;
        var cellHeight = this.renderer.dimensions.actualCellHeight || 17;
        var parentElementStyle = window.getComputedStyle(this.element.parentElement);
        var parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
        var parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
        var elementStyle = window.getComputedStyle(this.element);
        var elementPadding = {
                top: parseInt(elementStyle.getPropertyValue('padding-top')),
                bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
                right: parseInt(elementStyle.getPropertyValue('padding-right')),
                left: parseInt(elementStyle.getPropertyValue('padding-left'))
        };
        var elementPaddingVer = elementPadding.top + elementPadding.bottom;
        var elementPaddingHor = elementPadding.right + elementPadding.left;
        var availableHeight = parentElementHeight - elementPaddingVer;
        var availableWidth = parentElementWidth - elementPaddingHor - this.viewport.scrollBarWidth;
        var geometry = {
                cols: Math.floor(availableWidth / cellWidth),
                rows: Math.floor(availableHeight / cellHeight)
        };
                // This is still sometimes NaN, NaN !?
        return geometry;
}

Terminal.prototype.fit = function() {
    var geometry = this.proposeGeometry();

    if (geometry) {
        if (this.rows !== geometry.rows || this.cols !== geometry.cols) {
            this.renderer.clear();
            this.resize(geometry.cols, geometry.rows);
        }
    }
}

Je viens de remarquer l'exigence selon laquelle xterm exige que le parent soit visible lorsque open est appelé . Nous avons peut-être tort, alors j'essaierai d'y jeter un deuxième coup d'œil plus tard.

Je vois. Je ne sais même pas comment garantir cela avec etch et atom . Je ne sais pas s'il existe une sorte de crochet, comme onParentVisible ?

J'ai trouvé une solution sur la base de ce que vous avez dit.

J'ai déplacé terminal.open dans le gestionnaire de redimensionnement. Ça a marché. Ainsi, le composant est créé sur un dom virtuel, puis une fois ajouté au parent réel, le redimensionnement est invoqué et il configure le terminal réel.

Je viens de remarquer l'exigence selon laquelle xterm exige que le parent soit visible lorsque open est appelé. Nous avons peut-être tort, alors j'essaierai d'y jeter un deuxième coup d'œil plus tard.

Oui, vous devriez pouvoir contourner ce problème en vérifiant si l'élément est attaché avant d'appeler fit :

if (term.element) {
  term.fit();
}

Ainsi, le composant est créé sur un dom virtuel

Dans ce cas, vous aurez besoin d'un autre moyen de gérer cela, peut-être en vérifiant s'il est vraiment attaché (pourquoi est-il attaché à un dom virtuel ?)


Pour clore ce problème, nous devrions peut-être ajouter une vérification nulle sur certaines propriétés de dimensions à l'intérieur de fit afin qu'il n'essaye pas de dimensionner à Infinity ?

Même si term.element existe, l'élément parent peut ne pas l'être, il échoue donc toujours.

pourquoi est-il attaché à un dom virtuel ?

Parce qu'il est utilisé dans Etch/atom.

https://atom.io/packages/script-runner

Je ne sais pas s'il y a une meilleure façon de le faire.

@Tyriar Tout comme, xterm publie une excellente bibliothèque, et la bibliothèque a un bug dangereux qui provoque le plantage de mon navigateur, et le groupe xterm dit simplement au développeur de faire attention à cela.
Bien sûr, je peux ajouter une vérification. Mais je pense que la plupart des développeurs ne feraient pas attention à ce bogue, y compris le groupe xterm. Et nous pourrions facilement corriger ce bogue en ajoutant du code de protection pour l'empêcher (par exemple : définir un nombre maximal de lignes et de colonnes par défaut). pourquoi on ne le fait pas ?

Vous devez absolument vérifier qu'il n'allouera qu'un nombre fini de lignes et de colonnes... Il n'est pas nécessaire que ce soit un maximum... il suffit de vérifier que les arguments ne sont pas Infinity.

@ioquatix ouais, peut-être n'avons-nous pas besoin du maximum, ajoutez un maximum de lignes juste une solution de ma proposition. Je veux dire que xterm devrait ajouter un code de protection. Ne vous contentez pas de dire à des milliers de développeurs d'ajouter un code de vérification.
La cause première de ce bogue est que ces lignes n'avaient pas de code de protection.
https://github.com/xtermjs/xterm.js/blob/master/src/Terminal.ts#L1941
https://github.com/xtermjs/xterm.js/blob/master/src/Buffer.ts#L150

Je pense qu'il est raisonnable d'avoir des contrôles proactifs. Il ne devrait pas être possible que les entiers soient NaN, mais nous y sommes, c'est JavaScript. Il est donc logique de s'assurer que les nombres ne sont pas, disons, négatifs, NaN, Infinity, etc.

Le but de l'ajout de code de contrôle est d'améliorer la robustesse du programme. J'ai évidemment xterm pas assez robuste. Maintenant, s'il ne s'agit que d'erreurs normales, le navigateur signalera des erreurs, nous pouvons facilement les localiser et les résoudre. Mais cette erreur fait planter le navigateur, elle est très difficile à déboguer.

@kevinwon1985 J'ai dit de fermer ce problème avec un PR qui empêche les valeurs invalides :

Pour clore ce problème, nous devrions peut-être ajouter une vérification nulle sur certaines propriétés de dimensions à l'intérieur de l'ajustement afin qu'il n'essaie pas de dimensionner à l'infini?

Je ne veux pas ajouter de valeur maximale, mais comme cela complique l'API pour peu de raisons, je n'ai jamais rencontré de problèmes avec la définition de la taille sur un si grand nombre.

@Tyriar Aucune valeur maximale n'est correcte. Je pense qu'ajouter simplement un chèque à l'intérieur de fit n'est pas suffisant. D'autres développeurs peuvent faire la même erreur lorsqu'ils appellent resize , tout comme le font l'add-on fit. Devrions-nous donc ajouter un chèque à l'intérieur de resize ?

1/ Je pense que si la taille n'est pas valide, cela devrait être une erreur.

2/ Si la taille est trop grande pour la mémoire disponible de l'ordinateur, cela devrait aussi être une erreur, pas un plantage du navigateur.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

Tyriar picture Tyriar  ·  4Commentaires

pfitzseb picture pfitzseb  ·  3Commentaires

jerch picture jerch  ·  3Commentaires

7PH picture 7PH  ·  4Commentaires

tandatle picture tandatle  ·  3Commentaires