7

Chrome on iOS appears to create an XMLHttpRequest object when you include an external JavaScript file. It seems to assign this object to a global variable with the identifier a, overwriting anything you may have already had in there.

Test case:

  • HTML file (test.html):

    <!-- ... -->
    <script>
        var a = 1; // Value is not important for this demonstration
    </script>
    <script src="test.js"></script>
    <!-- ... -->
    
  • External JavaScript file (test.js):

    setTimeout(function () {
        document.write(a); // [object XMLHttpRequest]
        a.onreadystatechange = function () {
            document.write(a.readyState); // Alternates between "1" and "4"
        };
    }, 100);
    

The XMLHttpRequest appears to repeatedly make a request (to somewhere... routing the device connection through a proxy and monitoring requests doesn't show up anything) and the onreadystatechange event handler is repeatedly executed.

Some more observations:

  • This only seems to happen some time after page load (hence the setTimeout)
  • When it does happen, there is also a window.__gchrome_CachedRequest property
  • __gchrome_CachedRequest === a
  • It doesn't seem to happen in older versions of iOS Chrome (not sure which version it first occurs in though)

Has anyone come across this before? Is there a way I can stop it from happening (Note... I cannot rename a)? If anyone knows why it does this I would love to find out.


Update

I've just noticed that this actually happens with inline scripts too, not just when you include an external script. I didn't notice this initially because I didn't have the setTimeout call in there. So it looks like it actually just always happens some time after page load.

James Allardice
  • 164,175
  • 21
  • 332
  • 312
  • 1
    Note - the `document.write` calls are just in there to easily output to the page. Change them to `alert`, or `innerHTML`, or anything else if you like. The effect is the same. – James Allardice Feb 27 '13 at 14:25
  • That's a really bizarre behaviour, which I can't seem to replicate on my local. One thing I would recommend is testing given site on a clear Chrome outside your network (some plugins interrupt "normal" workings of js, ie. Skype, and some network providers add their stuff as well). – eithed Feb 27 '13 at 14:34
  • @eithed - I've tested it on clean Chrome installs on 3 different devices (iPhone 3 running iOS 5, and iPad 2 and iPod Touch both running iOS 6). They are all test devices with no unnecessary plugins. However, this is all with the latest version of Chrome (23). – James Allardice Feb 27 '13 at 14:36
  • Have you filed a bug report? That would be one step towards fixing the issue, sometimes the developers can offer advice and work arounds. – Andy E Feb 27 '13 at 14:38
  • @AndyE - Not yet but I definitely will. Thought I'd see if anyone here had come up against it before first. – James Allardice Feb 27 '13 at 14:39
  • @JamesAllardice: you can't change the variable name, but could you change its value? The idea being that you could use ES5 to define it as a read-only property of the `window` object, which should prevent the variable being overwritten by Chrome. – Andy E Feb 27 '13 at 14:43
  • @AndyE - Unfortunately I've already tried that, and it doesn't work. – James Allardice Feb 27 '13 at 14:45
  • @AndyE - However, what *does* work is accessing `a` in my own scripts as `window.a`. So Chrome appears to be creating some kind of shadow scope below `window`. – James Allardice Feb 27 '13 at 14:46
  • @JamesAllardice: hmm that indicates a global eval of the script and a `with` statement to layer the additional scope between the script and the `window` object. I've seen the developer tools do stuff like this... is it possible that this is because you have remote debugging attached? – Andy E Feb 27 '13 at 14:48
  • @AndyE - No, there's no remote debugging (as far as I know Chrome on iOS doesn't support it since it's just a UIWebView - which makes the global eval and `with` approach quite likely... to allow Chrome to proxy native methods of `window`). – James Allardice Feb 27 '13 at 14:52

1 Answers1

2

From the discussion we had in comments, it looks like Chrome is using a with statement to layer some of its own stuff over the top of window properties. It probably does this because those properties would not normally be overwritable. The delay is most likely down to how soon Chrome can inject its own script into the UIWebView (browsers on iOS are forced to use the underlying UIWebView object rather than their own engines) after a page starts loading.

If all this speculation is actually true, then there's simple solution, other than to access window.a instead of a or take the power back and use your own with statement:

with (window)
    eval(myScript);

Hardly a solution anyone would relish implementing, but might be your only option.

Andy E
  • 338,112
  • 86
  • 474
  • 445
  • 1
    I think our speculation is probably the case so I'll mark this as accepted. I've raised a [Chromium issue](http://code.google.com/p/chromium/issues/detail?id=178737) so we will see if there's any "official" response. Thanks for your help. – James Allardice Feb 27 '13 at 15:11
  • @JamesAllardice: no problem at all, I hope it gets fixed for you soon. – Andy E Feb 27 '13 at 15:27