Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a Safari page breaking iOS rendering?

I know the title is not that explanatory but here is the story: I am developing a browser game, mostly using JavaScript and the Mapbox library.

Everything works well on desktop, Android and iOS but one problem appears on iOS: after letting the game run for a few minutes the phone suddenly begins to have graphic artifacts and display most of the text scrambled.

Here are some photos of what the phone begins too look like: enter image description here enter image description here enter image description here

My question is: what exactly in my code can cause this? A memory leak? (LE: it turned out to actually be a memory leak)
The real question is: How comes that you can almost brick the entire phone by simply browsing a web page? Shouldn't Safari stop this, or at least the iOS ?

This is not a problem with this specific device, as this problem can be reproduced on different iPhone devices. (I'm not so sure about different iOS versions).

How I can reproduce the error:

  1. Open the game (inside Safari).
  2. Let it run for 3-4 minutes.
  3. Slide down the notification center and everything goes crazy.
    I have added a YouTube video showing how I can reproduce the error (on my iPhone 5C).
    It seems that the issue first appears in the notification center (if you swipe down the menu from the top).
    As for now, this problem seems to only occur on iPhone 5C iOS 9.2.1 (13D15). It also occurs on the new iOS 9.3 version.

In order to fix this issue I have to:

  1. Close the Safari application (in which the game tab is open).
  2. Lock the phone. After unlocking it everything is back to normal.

Some details about the game itself:

  1. The game shows a Mapbox map and some units over it (markers).
  2. A Node.js server runs at 1 tick/second and after each tick the updated game state is sent to the browser through Socket.io.
  3. Every time the browser receives the game state it updates the markers accordingly.
  4. *The game might also update markers if you zoom in or out or if you select them.

EDIT2: Found the memory leak (as expected). After fixing this leak (check for undefined _icon) the issue no longer occurs. This means, that somewhere along those lines the Safari/iOS bug is triggered.

Here is what exactly was being called each tick, for each unit that was clustered (was hidden and grouped with others inside a 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); 

And this is the icons array:

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

circleProgress call is from this library: https://github.com/kottenator/jquery-circle-progress

DEMO

Yay, I have been able to create a jsFiddle that reproduces the bug: https://jsfiddle.net/cte55cz7/14/ Open on Safari on iPhone 5C and wait a couple of minutes. On iPhone 6 and iPad mini the page crashes (as expected due to the memory leak)

Here's the same code in a HasteBin, for anyone who doesn't want to run it.

like image 326
XCS Avatar asked Mar 03 '16 20:03

XCS


People also ask

How do I fix Safari glitch on my iPhone?

You can clear website data occasionally to improve Safari performance. Go to Settings > Safari. Tap Clear History and Website Data. Tap Clear History and Data to confirm.

Why is my Safari messed up on iPhone?

One of the simplest and most common fixes for Safari not loading, or crashing, on an iPhone is turning off Safari Suggestions. While we aren't sure why this works, many users have reported that turning this off fixes the problem. To turn off suggestions, head to Settings > Safari > Safari Suggestions and turn this off.

Why is my Safari messed up?

Sometimes the Safari problem may be caused by incorrect network settings. Therefore, you can try to fix your Safari issue by resetting all Network settings to default. To reset network settings, just go to Settings > General > Reset > Reset Network Settings.


1 Answers

This memory leaks is probably due to how 'WebKit’s JS Engine' works [safari webkit-javascript llvm]

and really looks like to be a virtual memory buffer-overflow, having a direct impact on the remaining RAM (shared and used also by iOS to store User Interface graphical elements)

Relatively to the piece of code: "[...]finding jQuery memory leaks is easy. Check the size of $.cache. If it’s too large, inspect it and see which entries stay and why. [...]" (http://javascript.info/tutorial/memory-leaks)

Let me expect that it is relative to this for loop :

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); } 

Assuming inspection is done, and also assuming the fact that you find the entries, you may want to clean the data manually with removeData() or you may use first $elem.detach() and then put $(elem).remove() in setTimeout.

like image 81
A STEFANI Avatar answered Oct 22 '22 18:10

A STEFANI