¿Por qué una página de Safari rompe la representación de iOS?

79

Sé que el título no es tan explicativo, pero aquí está la historia: estoy desarrollando un juego de navegador, principalmente usando JavaScript y la biblioteca Mapbox.

Todo funciona bien en el escritorio, Android e iOS, pero aparece un problema en iOS: después de dejar que el juego se ejecute durante unos minutos, el teléfono de repente comienza a tener artefactos gráficos y muestra la mayor parte del texto codificado.

Aquí hay algunas fotos de cómo comienza a verse el teléfono: ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Mi pregunta es: ¿qué exactamente en mi código puede causar esto? ¿Una fuga de memoria? ( LE : resultó ser en realidad una pérdida de memoria)
La verdadera pregunta es: ¿Cómo es que casi se puede bloquear todo el teléfono simplemente navegando por una página web? ¿No debería Safari detener esto, o al menos iOS?

Esto no es un problema con este dispositivo específico, ya que este problema se puede reproducir en diferentes dispositivos iPhone. (No estoy tan seguro de las diferentes versiones de iOS).

Cómo puedo reproducir el error:

  1. Abre el juego (dentro de Safari).
  2. Déjelo funcionar durante 3-4 minutos.
  3. Desliza hacia abajo el centro de notificaciones y todo se vuelve loco.
    Agregué un video de YouTube que muestra cómo puedo reproducir el error (en mi iPhone 5C).
    Parece que el problema aparece primero en el centro de notificaciones (si desliza el menú hacia abajo desde la parte superior).
    Por ahora, este problema parece ocurrir solo en iPhone 5CiOS 9.2.1 (13D15). También ocurre en la nueva versión de iOS 9.3.

Para solucionar este problema, tengo que:

  1. Cierra la aplicación Safari (en la que está abierta la pestaña del juego).
  2. Bloquea el teléfono. Después de desbloquearlo, todo vuelve a la normalidad.

Algunos detalles sobre el juego en sí:

  1. El juego muestra un mapa Mapbox y algunas unidades sobre él (marcadores).
  2. Un servidor Node.js se ejecuta a 1 tick / segundo y después de cada tick, el estado del juego actualizado se envía al navegador a través de Socket.io.
  3. Cada vez que el navegador recibe el estado del juego, actualiza los marcadores en consecuencia.
  4. * El juego también puede actualizar los marcadores si acercas o alejas el zoom o si los seleccionas.

EDIT2: encontró la pérdida de memoria (como se esperaba). Después de arreglar esta fuga (verifique el undefinedícono _), el problema ya no ocurre. Esto significa que en algún lugar a lo largo de esas líneas se activa el error de Safari / iOS.

Esto es exactamente cómo se llamaba cada tick, para cada unidad que estaba agrupada (estaba oculta y agrupada con otras dentro de un MarkerCluster):

    var $icon = $(marker._icon); // marker._icon is undefined because of the clustering

    $icon.html('');

    $icon.append($('<img class="markerIcon" src="' + options.iconUrl + '" />'));

    var iconX = 10;
    var iconY = -10;
    var iconOffset = 0;

    for(var v in this.icons) {
        this.icons[v].css('z-index', + $icon.css('z-index') + 1);
        this.icons[v].css('transform', 'translate3d(' + iconX + 'px,' 
                                + (iconY + iconOffset) + 'px,' + '0px)');
        iconOffset += 20;

        this.icons[v].appendTo($icon);
    }

    // Fire rate icons
    this.attackRateCircle = $('<div class="circle"></div>');
    this.attackRateCircle.circleProgress({
        value: 0,
        size: 16,
        fill: { color: "#b5deff" },
        emptyFill: 'rgba(0, 0, 0, 0.5)',
        startAngle:  -Math.PI / 2,
        thickness: 4,
        animation: false,
    });
    this.attackRateCircle.hide();

    // Create and display the healthbar
    this.healthBar = $('<div>').addClass('healthBar ');
    this.healthBar.css('z-index', $icon.css('z-index'));
    this.healthBarFill = $('<span class="fill">');
    this.healthBar.append(this.healthBarFill);

    $icon.append(this.healthBar);
    $icon.append(this.attackRateCircle);

Y esta es la iconsmatriz:

this.icons = {
    attack_order: $('<img src="img/attack.png" class="status_icon">'),
    attack: $('<img src="img/damage.png" class="status_icon icon_damage">'),
    hit: $('<img src="img/hit.png" class="status_icon icon_hit">'),
};

circleProgressla llamada es de esta biblioteca: https://github.com/kottenator/jquery-circle-progress

MANIFESTACIÓN

Sí, he podido crear un jsFiddle que reproduce el error: https://jsfiddle.net/cte55cz7/14/ Abrir en Safari en iPhone 5C y esperar un par de minutos. En iPhone 6 y iPad mini, la página se bloquea (como se esperaba debido a la pérdida de memoria)

Aquí está el mismo código en un HasteBin, para cualquiera que no quiera ejecutarlo.

XCS
fuente
2
PD: Publicado en SO después de haber sido votado en contra por publicar la pregunta en SuperUser. Espero que este sea considerado el lugar adecuado para hacer esta pregunta.
XCS
2
@wottle Probé en mi iPhone 5C, y otra persona lo probó (en otro continente: D) en otro iPhone, pero creo que su modelo también es 5C (y él fue quien realmente me habló de esos artefactos). Lo probaré en un iPhone 6 y un iPad mini mañana.
XCS
2
No se reproduce en mi iPhone 6. Podría ser un error en CG. ¿Puede publicar toda la información de la versión de iOS junto con la información del hardware?
Fresheyeball
2
¡Guauu! Y pensé que el desgarro de la pantalla en Windows 10 Mobile era malo ...
BoltClock
6
A) Ni siquiera estoy enojado. Eso es increíble. B) Me pregunto si esto realmente podría usarse como un exploit, ya que claramente estás accediendo a la memoria y no deberías poder hacerlo, y C) Creo que es interesante que todo esté siempre inclinado hacia la izquierda. Eso me sugiere que, por alguna razón, estás aumentando los tonos (también conocidos como pasos) de algunas texturas o modificando la lógica de tono de la GPU. Sin embargo, definitivamente es un error de Safari / iOS / firmware.
0x24a537r9

Respuestas:

1

Esta pérdida de memoria probablemente se deba a cómo funciona el motor JS de WebKit [safari webkit-javascript llvm]

y realmente parece ser un desbordamiento de búfer de memoria virtual, que tiene un impacto directo en la RAM restante (compartida y utilizada también por iOS para almacenar elementos gráficos de la interfaz de usuario)

Relativamente al fragmento de código: "[...] encontrar fugas de memoria de jQuery es fácil. Verifique el tamaño de $ .cache. Si es demasiado grande, inspecciónelo y vea qué entradas permanecen y por qué. [...]" ( http://javascript.info/tutorial/memory-leaks )

Permítanme esperar que sea relativo a este bucle for :

for(var v in this.icons) {
    this.icons[v].css('z-index', + $icon.css('z-index') + 1);
    this.icons[v].css('transform', 'translate3d(' + iconX + 'px,' 
                            + (iconY + iconOffset) + 'px,' + '0px)');
    iconOffset += 20;

    this.icons[v].appendTo($icon);
}

Suponiendo que se realiza la inspección, y también asumiendo el hecho de que encuentra las entradas, es posible que desee limpiar los datos manualmente con removeData () o puede usar primero $ elem.detach () y luego poner $ (elem) .remove () en setTimeout.

UN STEFANI
fuente