Dlaczego strona Safari psuje renderowanie w iOS?

79

Wiem, że tytuł nie jest zbyt objaśniający, ale oto historia: tworzę grę przeglądarkową, głównie przy użyciu JavaScript i biblioteki Mapbox.

Wszystko działa dobrze na desktopie, Androidzie i iOS, ale jeden problem pojawia się na iOS: po kilku minutach uruchomienia gry telefon nagle zaczyna mieć artefakty graficzne i wyświetla większość zaszyfrowanego tekstu.

Oto kilka zdjęć, jak zaczyna wyglądać telefon: wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Moje pytanie brzmi: co dokładnie w moim kodzie może to spowodować? Wyciek pamięci? ( LE : okazało się, że to wyciek pamięci)
. Prawdziwe pytanie brzmi: jak to się dzieje, że można prawie zepsuć cały telefon, po prostu przeglądając stronę internetową? Czy Safari nie powinno tego zatrzymać, a przynajmniej iOS?

Nie stanowi to problemu w przypadku tego konkretnego urządzenia, ponieważ ten problem można odtworzyć na różnych urządzeniach iPhone. (Nie jestem pewien co do różnych wersji iOS).

Jak mogę odtworzyć błąd:

  1. Otwórz grę (w Safari).
  2. Pozwól mu działać przez 3-4 minuty.
  3. Przesuń w dół centrum powiadomień i wszystko zwariuje.
    Dodałem film z YouTube pokazujący, jak mogę odtworzyć błąd (na moim iPhonie 5C).
    Wygląda na to, że problem najpierw pojawia się w centrum powiadomień (jeśli przesuniesz menu od góry w dół).
    Na razie ten problem wydaje się występować tylko w iPhone 5Csystemie iOS 9.2.1 (13D15). Występuje również w nowej wersji iOS 9.3.

Aby rozwiązać ten problem, muszę:

  1. Zamknij aplikację Safari (w której karta gry jest otwarta).
  2. Zablokuj telefon. Po odblokowaniu wszystko wraca do normy.

Kilka szczegółów na temat samej gry :

  1. Gra pokazuje mapę Mapbox i kilka jednostek nad nią (znaczniki).
  2. Serwer Node.js działa z szybkością 1 taktu na sekundę, a po każdym taktowaniu zaktualizowany stan gry jest wysyłany do przeglądarki za pośrednictwem Socket.io.
  3. Za każdym razem, gdy przeglądarka otrzymuje stan gry, odpowiednio aktualizuje znaczniki.
  4. * Gra może również aktualizować znaczniki, jeśli powiększysz lub pomniejszysz lub jeśli je wybierzesz.

EDIT2: Znaleziono wyciek pamięci (zgodnie z oczekiwaniami). Po naprawieniu tego wycieku (sprawdź, czy nie ma undefined_icon) problem już nie występuje. Oznacza to, że gdzieś w tych liniach pojawia się błąd Safari / iOS.

Oto, co dokładnie nazywano każdym tikiem dla każdej jednostki, która została zgrupowana (została ukryta i zgrupowana z innymi w 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);

A to jest iconstablica:

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">'),
};

circleProgresspołączenie pochodzi z tej biblioteki: https://github.com/kottenator/jquery-circle-progress

PRÓBNY

Tak, udało mi się stworzyć jsFiddle, które odtwarza błąd: https://jsfiddle.net/cte55cz7/14/ Otwórz w Safari na iPhonie 5C i poczekaj kilka minut. Na iPhonie 6 i iPadzie mini strona ulega awarii (zgodnie z oczekiwaniami z powodu wycieku pamięci)

Oto ten sam kod w HasteBin dla każdego, kto nie chce go uruchamiać.

XCS
źródło
2
PS: Wysłano SO po tym, jak zostałem odrzucony za opublikowanie pytania SuperUser. Mam nadzieję, że uznano to za właściwe miejsce do zadawania tego pytania.
XCS
2
@wottle Testowałem na moim iPhonie 5C, a inna osoba testowała (na innym kontynencie: D) na innym iPhonie, ale myślę, że jego model też jest 5C (i to on faktycznie powiedział mi o tych artefaktach). Jutro przetestuję na iPhonie 6 i iPadzie mini.
XCS
2
Nie odtwarza się na moim iPhonie 6. Może to być błąd w CG. Czy możesz opublikować wszystkie informacje o wersji ios wraz z informacjami o sprzęcie?
Fresheyeball
2
Łał! I pomyślałem, że zrywanie ekranu w Windows 10 Mobile było złe ...
BoltClock
6
A) Nie jestem nawet zły. To jest wspaniałe. B) Zastanawiam się, czy rzeczywiście można to wykorzystać jako exploit, ponieważ wyraźnie uzyskujesz dostęp do pamięci, której nie powinieneś mieć, i C) Myślę, że to interesujące, że wszystko zawsze przechyla się w lewo. Sugeruje to dla mnie, że z jakiegoś powodu zwiększasz wysokość (zwaną także krokami) niektórych tekstur lub modyfikujesz logikę wysokości dźwięku GPU. Zdecydowanie błąd Safari / iOS / oprogramowania układowego.
0x24a537r9

Odpowiedzi:

1

Ten wyciek pamięci jest prawdopodobnie spowodowany działaniem „silnika JS WebKit” [safari webkit-javascript llvm]

i naprawdę wygląda na przepełnienie bufora pamięci wirtualnej, mające bezpośredni wpływ na pozostałą pamięć RAM (współdzieloną i używaną również przez iOS do przechowywania elementów graficznych interfejsu użytkownika)

W odniesieniu do fragmentu kodu: „[...] znajdowanie wycieków pamięci jQuery jest łatwe. Sprawdź rozmiar $ .cache. Jeśli jest za duży, sprawdź go i zobacz, które wpisy pozostają i dlaczego. [...]” ( http://javascript.info/tutorial/memory-leaks )

Spodziewam się, że jest to relatywne do tej pętli 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);
}

Zakładając, że inspekcja jest zakończona, a także zakładając, że znajdziesz wpisy, możesz chcieć wyczyścić dane ręcznie za pomocą removeData () lub możesz najpierw użyć $ elem.detach (), a następnie wstawić $ (elem) .remove () w setTimeout.

STEFANI
źródło