1

I have built a mobile webapp in html/js and I am using the 'window.onerror' to track client side js errors and report them to the server.

Recently I noticed that I receive many reports of ScriptError on line 0.

A quick google search brought me to discussions like Cryptic "Script Error." reported in Javascript in Chrome and Firefox and https://ravikiranj.net/posts/2014/code/how-fix-cryptic-script-error-javascript/

For most people such errors seem to be triggered by failures in scripts that are included from a foreign origin. But unfortunately that's not the case for me.

After many hours of debugging I came up with the following minimal example to reproduce the error:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Test</title>
  <meta name="google" content="notranslate"/>

  <script type="text/javascript">
  function init() {
    window.onerror = function() {
      alert(JSON.stringify(arguments));
    }
  }
  </script>
</head>
<body onload="init()">
  <h1>Hello</h1>
  <svg width="300" height="300">
    <a href="#">
      <rect x="50" y="50" width="200" height="200" />
    </a>
  </svg>
  <p>
  <a href="#">Link</a>
  </p>
</body>
</html>

As we can see it's a simple html page with no javascript at all except for the onerror handler itself.

In Safari (ios 12.4.1) no error is triggered. But in both Google Chrome for iOS and Firefox for iOS a ScriptError on line 0 is reported each time the link inside the <svg> element is tapped - but not when tapping the link outside the <svg>. Note that no js event handler is attached to the <svg>.

A live version of the code is online at https://static.laszlokorte.de/scripterror.php

I know that all third party browsers on iOS actually just use safari under the hood extending in with a few custom features. I suspect that those custom features cause the error but I have no idea how to track it down further.

Why does clicking a link inside a plain svg element causes an error in iOS Chrome and Firefox but not in Safari? Is there a way to connect the Safari Webdev tools to mobile browsers other than native Safari?

Update

After taking a deeper look at both the Chrome(iOS) and Firefox(iOS) source code I now found the cause of the problem. Both browsers inject javascript code that runs on touchstart and processes the event's target node. Both browsers check if an '' or '' element has been touched and try to access their their href or src attribute respectively. They both then try to send the attributes values via webkit.postMessage to the host application.

The error seems to be caused by the fact that an <a> element inside an <svg> is actually an SVGAElement and it's href attribute is not a String but an SVGAnimatedString which (other than String) can not be cloned to be sent to another process.

The corresponding code for Firefox can be found here: https://github.com/mozilla-mobile/firefox-ios/blob/826cb396a9eb225b7eb667b47b245e2d98d26ed8/Client/Frontend/UserContent/UserScripts/AllFrames/AtDocumentEnd/ContextMenu.js#L14-L35

The code for Chrome is here: https://github.com/chromium/chromium/blob/master/ios/web/web_state/js/resources/all_frames_context_menu.js#L136-L139

So for Chrome the solution is actually simple because it allows to explicitly opt-out of all this by setting the css property -webkit-touch-callout: none (You can see the check for that in the linked file). For example like this:

@namespace svg url(http://www.w3.org/2000/svg);

svg|a {
  -webkit-touch-callout: none;
}

For Firefox I have not found a easy solution yet.

Update 2

For Firefox I now came up with the following monkey patch solution:

(function() {
    var origClosest = SVGElement.prototype.closest;
    SVGElement.prototype.closest = function (sel) {
      var c = origClosest.apply(this, arguments);
      if(c && sel === 'a' && c.namespaceURI === this.namespaceURI) {
        return c.parentNode && c.parentNode.closest(sel);
      } else {
        return c;
      }
    };
  })();

I override the closest method (that Firefox itself uses to find the touched <a>) on an SVG elements with my own method that skips all <a> elements that belong to the SVG namespace.

Additionally I submitted an issue in the Firefox repo at Github and an issue for Chromium.

Laszlo Korte
  • 1,179
  • 8
  • 17
  • Your init function is being defined after the onload attribute tries to use it. Maybe try put the script in the head element? – evolutionxbox Sep 24 '19 at 22:39
  • That does not change anything. The onerror listener is attached correctly. The question is why it gets triggered when tapping the link inside the svg. – Laszlo Korte Sep 24 '19 at 22:44
  • I can’t seem to replicate your issue on iOS 13. Will try on a simulator at a later date. – evolutionxbox Sep 24 '19 at 22:52

0 Answers0