3

I intercept AJAX requests in my site by altering the XMLHttpRequest.prototype open and send methods. This method worked without any troubles in all the browsers I tested. However, when it comes to Chrome for iOS (iPhone) the code has the weirdest bug: it's like it continuously fire the code I changed in the prototype (ending up crashing, obviously).

Here's a super-minimal example of what I am doing:

var open = XMLHttpRequest.prototype.open; // Caching the original
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
    alert('open'); // Here is my code
    open.call(this, method, url, async, user, pass); // Calling the original
 };

I've assembled a little JSBin doing just that you can visit with your Chrome on iOS: Demo

According to this answer, the code I'm using (essentially the same as the one OP in that answer is going to use) is safe and there should be no reason to worry. And, as a matter of fact, Chrome for iOS is the only browser which behaves weirdly.

This has been driving me nuts for two days, any suggestion or workaround appreciated.

Community
  • 1
  • 1
NinGen ShinRa
  • 651
  • 5
  • 16
  • what happens on `XMLHttpRequest.prototype.open = function() { alert("moo"); open.apply(this, arguments); }` instead? `call` doesn't take the operation context as first argument, it treats that as the first function argument. `appy`, however, does. – Mike 'Pomax' Kamermans Mar 28 '15 at 01:25
  • As far as I know [`call` does take the operation context as the first argument](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call), the only difference being in the rest of the arguments. But, anyways, I tried to edit it using `apply`, but I get the same error. In all fairness, I tried to entirely remove that line, and I still get the same problem: it seems that editing the `prototype` of `XMLHttpRequests` does that. – NinGen ShinRa Mar 29 '15 at 18:57
  • ah good point. Do other IOS browsers work though? Because all browsers on IOS are essentially "usability interfaces" on top of the same render engine. Also, is it specific versions of IOS? – Mike 'Pomax' Kamermans Mar 29 '15 at 20:51
  • Yes, I tried other iOS browsers such as as Mercury and Opera Mini. I've heard that Chrome for iOS has quirks way beyond the normal things you'd expect from a WebView. My iOS version is 8.2, but I've tried with a friend's iPhone on iOS 7 something. Chrome version is latest at: 41.0.2272.58. – NinGen ShinRa Mar 30 '15 at 00:04
  • Your JSBin does not appear to call the open function. Can you confirm if you have the issue with [this bin](http://jsbin.com/hofaco)? If so, do you also have the issue with [this bin](http://jsbin.com/wimewe)? – Alexander O'Mara Mar 30 '15 at 01:09
  • Thank you for your answer: yes, my JSBin doesn't even call the open function (though an earlier revision of it does, as mentioned in the code snipped above), and that was my point. You don't even need to call it: barely changing the `prototype` causes an infinite loop. The second one was a really smart solution, but unfortunately the trouble still is there. – NinGen ShinRa Mar 30 '15 at 17:10
  • @NinGenShinRa I've verified my solution for you on Chrome. – Drakes Apr 05 '15 at 23:47

1 Answers1

4

How to Intercept AJAX requests on Chrome for iOS

This is XMLHttpRequest interception code that works on most browsers:

(function(open) {
  XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
    // Intercept the open request here
    alert("Intercepted: " + url);
    open.apply(this, arguments);
  };
})(XMLHttpRequest.prototype.open);

xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","http://google.com",true);
xmlhttp.send();

There is a problem in Chrome for iOS. It has been raised and investigated below. I will provide an explanation for the "repeated open() calls" bug, a demonstration, and a workaround.

From the last reference:

On page load, Chrome makes two asynchronous requests to services that it is presumably running locally. By the sound of the URLs it’s requesting, these services are used to ensure the safety of the page you are accessing.

And here is a screenshot of one such local URL Chrome is trying to access (Demo):

Repeated Chrome calls

Chrome calls XMLHttpRequest.open() periodically on it's own. These repeated calls to the interception code are not caused by the interception code itself; they are caused by unrelated and repeated calls from the Chrome browser. I've identified two such URLs. There may be others.

From my research, this workaround makes the XMLHttpRequest code interception work on Chrome for iOS. See this JSBin testing demo. It will demonstrate just how these repeated calls come about too. Essentially, the interception code should ignore URLs used by Chrome.

(function(open) {
  XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
    var d1 = document.getElementById('urls');

    // Avoid intercepting Chrome on iOS local security check urls
    if(url.indexOf("/chromecheckurl") < 0 && url.indexOf("/chrome") !== 0) {
        // These are what we want to intercept
        d1.insertAdjacentHTML('beforeend', '<b>'+url+'</b><br/>');
    } else {
        // These are the internal Chrome requests - we can ignore them
        d1.insertAdjacentHTML('beforeend', '<i>'+url+'</i><br/>');
    }

    open.apply(this, arguments);
  };
})(XMLHttpRequest.prototype.open);


xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","http://google.com",true);
xmlhttp.send();

This is my best attempt at an explanation about this "repeated open() calls" bug on Chrome for iOS, and a workaround.

Drakes
  • 23,254
  • 3
  • 51
  • 94
  • @NinGenShinRa Kindly verify that this demo works for you on Chrome for iOS – Drakes Apr 07 '15 at 21:51
  • Thank you for the answer, but this is not the problem: jsfiddle probably makes something to avoid infinite alert loops, the [same code on jsbin](http://jsbin.com/fosowuqeci/1/), however, shows the problem above. I'm curious, why do you think this would have solved the problem, though? Sure, scoping is great (and that's what I do in my original code anyways), but what is the rationale behind this helping? (Asking so maybe I can avoid similar pitfalls in the future) – NinGen ShinRa Apr 08 '15 at 01:00
  • Oh, interesting! Let me investigate. I wanted you to confirm it worked before I explained in detail what I suspect is the problem. Maybe I can get that vote back ;) – Drakes Apr 08 '15 at 02:33
  • This is indeed the correct answer. The [Chrome issue](http://code.google.com/p/chromium/issues/detail?id=471591) that you linked was indeed filed after this question and uses my JSBin as a minimal example case. Thanks for the research! – NinGen ShinRa Apr 08 '15 at 18:57