Set a breakpoint in XHR in Chrome - javascript

I have a page that sends an XHR request when a form is submitted and I would like to get Chrome to break when it receives a response. It seems like the best way to accomplish this would be if Chrome has a javascript function that I can call that breaks execution but I've been unable to find anything like that so far. Is there another solution?
Edit:
I don't actually have a callback defined for the request so I can't set a breakpoint that way.
The request is being sent with this line of jquery code:
$.post(this.action, $(this).serialize(), null, "script");
where this is a form element. The null argument is where you would usually define a callback but with the "script" argument, raw javascript is returned by the server and then directly executed, so it seems the only way to break and step through the code is with the debugger; statement. This works, but when stepping through the code you can't actually see which line you are on so its a little awkward. I suspect that this is a limitation of Chrome's debugging tools.

drop down the chrome console (ctrl+shift+j) and type any of these:
Just rewrite the jquery ajax:
var prevajax = jQuery.ajax;
jQuery.ajax = function () { debugger; return prevajax.apply(jQuery, arguments); };
or if you are not using jQuery, rewrite the xhr class:
var prevxhr = XMLHttpRequest;
XMLHttpRequest = function (){debugger; prevxhr.apply(this, arguments);};
After it breaks, just press shift+f11 until you find the method which initiates the ajax request.

You can just set a breakpoint in the success callback and step through the debugger. To set a breakpoint:
Open "Developer Tools", and click the "Scripts" tab on the top.
Select the script that contains the success callback for your AJAX request.
Click the line number on the left hand side where you want the breakpoint. A blue arrow will show up indicating the breakpoint has been set.
Then make the AJAX request as you would, and the debugger will automatically stop at the breakpoint you set.
Alternatively, you can use the debugger statement to automatically invoke the debugger. So with this your success callback may look like:
success: function(data, textStatus, request) {
debugger; // pause execution and opens debugger at this point
...
}
Also checkout this nice article for debugging JavaScript.
However, the first approach is better as it doesn't require you to modify your code.

You can also go to scripts tab in developer tool, and on the right side you click on XHR Breakpoints and add new breakpoint with url filtering.
If you use jQuery, when breakpoint occurs you can use CallStack to find your function that called jQuery.ajax.

For me the solution was to use Initiator tab in the Network panel, it showed me the script that sent the request

ES6+
XMLHttpRequest = (Parent =>
class XMLHttpRequest extends Parent {
constructor() {
const onload = () => {
this.removeEventListener('load', onload);
debugger;
};
super(...arguments);
this.addEventListener('load', onload);
}
}
)(XMLHttpRequest);
ES5
XMLHttpRequest = (function (Parent) {
XMLHttpRequest.prototype = Parent;
return XMLHttpRequest;
function XMLHttpRequest() {
var self = this;
Parent.apply(this, arguments);
this.addEventListener('load', function () {
self.removeEventListener('load', onload);
debugger;
});
}
})(XMLHttpRequest);
This should let you inspect the request/response in the Network developer tool.
Note 01
ES5 syntax IS NOT to be understood as ES5+ mainly because function extends class ... isn't something that works at runtime. The class part will fail with Uncaught TypeError: Failed to construct 'XMLHttpRequest': Please use the 'new' operator. At that point, you may consider changing :
Parent.apply(this, arguments);
into
super(...arguments);
Note 02
Every solution here supposes that the library that issue the XHR request did not cache the XMLHttpRequest constructor [yet].
Note 03
The above code does not work with ES5 especially b/c of supper vs. .prototype inheritance syntax.
In order to circumvent thes issues, one may tap into the prototype. In the following code, I choosed to tap into the the xhr.send method.
ES5+
XMLHttpRequest.prototype.send = (function (send) {
return function () {
this.addEventListener('load', function onload() {
this.removeEventListener('load', onload);
debugger;
});
return send.apply(this, arguments);
};
})(XMLHttpRequest.prototype.send);
That said, the latter code didn't work for the issue that had me dig into this... Mainly because the internal Angular code registered an event listener (that fully redirects the browser) before even calling the xhr.send method. Not great, eh !?
That's it!

Since this question was asked (in 2010) chrome has added support for breakpoints on xhr (ajax, fetch). You can specify a substring for the url to set a conditional xhr breakpoint.
https://developer.chrome.com/docs/devtools/javascript/breakpoints/#xhr

Related

JavaScript - addEventListener is not defined

I am creating a chrome extension for blocking ads. Everything worked pretty fine so far. My goal is to:
Disable WebSocket
Disable console.log
Disable alert
Disable popups (window.open)
Disable onbeforeunload
Of course, it is supposed to block these functions only on specified websites. I searched on how to block a function. What I found is that the only way to disable a function using extension is to
In manifest.json add a content script init.js which will run at document_start (this will prevent any other code from being executed before my extension's code, so nobody can steal and save reference to these functions before I disable them)
From init.js inject code in webpage, so that my extension can access non-sandboxed environment
Override blacklisted functions
I did everything, but the problem was how to override functions. I found on EcmaScript7 specification that there is no way to detect if a function is proxy instance or not. So, I proxyfied every of these functions and it works as expected. Websites couldn't notice that I've overriden all of these functions.
However, it works for all functions expect addEventListener. In order to block onbeforeunload, I should add a proxy handler to it and track calls to check if listener name is onbeforeunload. I wrote the following script:
addEventListener = new Proxy(addEventListener, {
apply: (f, t, args) => {
if(args[0] !== 'onbeforeunload'){
f.apply(t, args);
}
}
});
This should filter calls and allow only calls which doesn't set onbeforeunload event. However, it throws ReferenceError: addEventListener is not defined. It surprised me a lot. I supposed that it is maybe because addEventListener is not yet initialized at document_start, but console.log(typeof addEventListener) logs function to console. How is that even possible?
I couldn't explain it. However, I tried to put it in a function and then to requestAnimationFrame to that function until addEventListener become accessible (I ecapsulated the above code in try...catch too). But, it turns out that modifying addEventListener always throws ReferenceError, while just accessing it never throws error.
I also tried to access it from developer console and from javascript:... method, but it behaves normally, I can modify it without any errors.
Edit
For sure, the problem isn't like this one, becuase I properly injected script into non-sandboxed (real) page environment and I have access to real window object.
addEventListener isn't a global Actually, it is, indirectly. It's a property of EventTarget.prototype (at least in Chrome). window has EventTarget.prototype in its prototype chain, which makes addEventListener a global.
So you'd need to proxy that instead, roughly (without your event name check, just for demonstration):
const save = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = new Proxy(save, {
apply: function(target, thisArg, args) {
console.log("Foo!");
return save.apply(thisArg, args);
}
});
document.body.addEventListener("click", function() {
console.log("Clicked");
});
<p>....</p>
Other notes:
The event name is beforeunload, not onbeforeunload.
You'll need to redefine the window.onbeforeunload property to prevent assignments to it, probably via Object.defineProperty.

TypeError: Not enough arguments to Window.postMessage

I am learning about the web worker. I am using the follow tutorial
https://developer.mozilla.org/en/docs/Web/Guide/Performance/Using_web_workers
So far, it works. I have the following code
var worker = new Worker('thing.js');
worker.addEventListener('message', function(e) {
alert("Worker said: " + e.data);
}, false);
worker.postMessage("Test me");
and in my thing.js file
self.addEventListener('message', function(e) {
self.postMessage(e.data);
}, false);
The above works fine.
However, I need to pass a message from within my thing.js back to my main js file, to demonstrate it passing back a progress update.
Again, the tutorial shows I can do this, so I have the following updated code
var worker = new Worker('thing.js');
worker.addEventListener('message', function(e) {
alert("Worker said: " + e.data);
}, false);
worker.postMessage("Test me");
and in my thing.js
self.addEventListener('message', function(e) {
self.postMessage(e.data);
}, false);
function DoThisThing() {
postMessage("I should ALSO be working but I am not.");
}
This fails with an error message (in FireBug):
TypeError: Not enough arguments to Window.postMessage.
I can't see what I've done wrong.
It would be convenient to have the full source code that triggered the issue, but I bet the problem was that besides instantiating the worker your code was also including the worker file via a script tag. Something like:
<script type="text/javascript" src="thing.js"></script>
So basically what you have is:
thing.js (worker)
self.addEventListener('message', function(e) {
self.postMessage(e.data);
}, false);
function DoThisThing() {
postMessage("I should ALSO be working but I am not.");
}
DoThisThing();
I assume you were calling DoThisThing() because otherwise you won't get the "TypeError: Not enough arguments to Window.postMessage" error.
If you run the code as it is it works, but you're also seeing the error.
TypeError: Not enough arguments to Window.postMessage.
Worker said: I should ALSO be working but I am not.
Worker said: Test me
So what was triggering the TypeError: Not enough arguments...? If you were sourcing the file from a script tag, the function gets executed two times actually. On the first call, there's an error.
When thing.js gets sourced by the script tag, DoThisThing() gets executed. That's the first call. DoThisThing() calls postmessage (same as self.postmessage). But this call gets resolved to window.postmessage. This method requires a second argument (targetOrigin).
However when the worker gets executed, the call to postmessage (same as self.postmessage) gets resolved to DedicatedWorkerGlobalScope.postmessage. This method doesn't require a second argument.
When you added a second argument to postmessage, you solved the issue for the first call but not the second. Thus you got a new error: "TypeError: Argument 2 of DedicatedWorkerGlobalScope.postMessage can't be converted to a sequence".
Summarizing, there are two types of postmessages, one that belongs to the window object and another one that belongs to the DedicatedWorkerGlobalScope object. A simple call to postmessage gets normally resolved by the DedicatedWorkerGlobalScope object. This call is convenient to communicate workers with their parent. window.postmessage requires a second parameter specifying the targetOrigin. This call is convenient for cross-origin communications.
In your case, the likely cause of the issue was sourcing the worker JavaScript file from a script tag. That caused postmessage to get resolved by window, which required a second argument.

Calling a Flex/AS3 Callback from Javascript

I have a Javascript API, which should be usable with GWT and Flex. Using the FABridge it is really easy to call Javascript methods from AS3 and vice versa. But when I try to register a callback to an AS3 method in my Javascript API I get stuck. Here is a short code sample:
public function initApp():void {
if (ExternalInterface.available) {
ExternalInterface.addCallback("foobar", foobar);
}
}
public function foobar():void {
//the callback function
Alert.show("Callback from API works!");
}
private function btnCallbackClicked():void {
ExternalInterface.call("testAPICallbackFromJS", Application.application.foobar);
}
And the simple JS method:
function testAPICallbackFromGWT(callback){
$clinit_26(); //added by the GWT compiler
alert('callback to be launched 3 2 1');
callback();
}
But this version does not work, because I always receive an empty function in my JS code. It seems that the FABridge is cutting the rest.
Then I tried a different approach. I wrote a little JS method, which takes the name of the function and creates the callback from the JS side.
registerFlexCallback = function(registerMethod, callback, id) {
/*
workaround to create a callback for an AS method, which can be called by Javascript
* registerMethod - Javascript method which shall be called for registration with the created callback as parameter
* callback - AS method that shall be called by Javascript (available over the FABridge interface)
* id - ID of the flash object (use Application.application.id in AS)
*/
var swf = document.getElementById(id);
eval(registerMethod + "(swf." + callback + ");");
};
This one works well with the Internet Explorer, but with no other browser. For example in Firefox I get the following error message:
NPMethod called on non-NPObject wrapped JSObject!
Can somebody tell me, what this error is about (maybe some kind of security issue)? Or does anyone have a better idea how to create callbacks for my AS3 methods which can be called by JS?
This is because functions don't serialize across the FABridge. Meaning in your
ExternalInterface.call("testAPICallbackFromJS", Application.application.foobar);
the second parameter will always be null. What I do is add a wrapper method on the HTML page via eval that points at my embed and therefore the added callback. So you have to add an extra, while annoying step:
ExternalInterface.addCallback("foobar", foobar);
var callBack:String = "";
var functionName:String = UIDUtil.createUUID;
callBack = "function " + functionName + "( ){ " +
"document.getElementById('applicationName').foobar(arguments);"+
"}";
ExternalInterface.call("eval", callback);
ExternalInterface.call("testAPICallbackFromJS", functionName);
The NPObject error you're seeing I'm pretty sure is a security error ( based on where it comes from in the FF code ) probably preventing you from dynamically injecting methods that can be eval'ed without the JS interpreter getting in the way.
I haven't even tried to compile the above so, hopefully you get the gist.
I notice two things right away
firstly it appears your ExternalInterface will die if the ExternalInterface is not ready.
public function initApp():void {
if (ExternalInterface.available) {
ExternalInterface.addCallback("foobar", foobar);
}
}
I would add a timout and then try again so that it tries again until Externalinterface is ready.
Also I don't see the function "foobar" in your javascript code. I see callback passed in as a variable but without varifying that it is in fact 'foobar' this is hte kind of thing that can make testing a misserable event.
function testAPICallbackFromGWT(callback){
$clinit_26(); //added by the GWT compiler
alert('callback to be launched 3 2 1');
callback();
}
I would simplify your testing example so that there are less moving parts.
// e.g. run just flash to javascript only
ExternalInterface.call("alert", "hello out there");
if that works
// establish the call from flash
ExternalInterface.addCallback("hello_out_there", foobar);
// and in javascript
alert(typeof('hello_out_there')); // will be 'function' if exists or undefined if ExternalInterface did not work
This way you can get a handle bit for bit what is working and where it breaks down.
Pay atention to the timing, if you can tigger your flash from button actions and your javascript from links you can illiminate a number of loading issues as well. of course you'll need to solve an autoload version for your launch but for testing manually triggered events can simplify things significantly.
also because it's javascript the browser is relevant.
I've seen consistent results in Firefox and Internet explorer that break down in safari and sometimes IE is the odd browser out.
Sometimes Firefox is the only one that breaks.
you just have to test them all.

how to invoke a dynamically loaded javascript function

I was implementing a on-demand script controller based on jquery's getscript, it looks like this:
function controller = function(){
var script = function(){
var scripts = {};
return {
load: function(jsurl){
$.getScript(jsurl, null);
},
run: function(js){
window[js].apply(this,null);
}
}
};
return {
script: script()
};
}
var ctlr = controller();
then here is a remote script with a function to be loaded - remote.js
function remotefunc(){
alert( 'remotefunc invoked' );
}
and here is how the whole thing supposed to work, in the main script:
ctlr.script.load( 'remote.js' ); // remote script successfully loaded
ctlr.script.run( 'remotefunc' ); // got an error, window['remotefunc'] undefined
but as you can see, 'remotefunc' is defined in the global 'window' scope, so the window object is supposed to be able to 'see' it.
I thought the problem was probably the closure stuff in the 'controller' definition, so I did a direct $.getScirpt without using the 'controller':
$.getScript( 'http://path/to/remote.js', function(){
window['remotefunc'].apply( this, null ); // this worked
} );
strange. So it is about the 'controller' implementation(I kind need it)! Anybody can help me out with this? How to fix the 'controller' implementation so the
window[js].apply(this,null);
can actually work?
Thanx.
The reason it's telling you window['remotefunc'] is undefined is because you are not giving it time to actually download and execute the remote script before attempting to call a function defined in it.
The remote script is loaded asynchronously, which means the script execution isn't paused while waiting for a response.
You will need to either re-implement the getScript method to be synchronous or somehow work your class around the fact that the function will not be available in any determinate amount of time.
EDIT: Just found another possible solution, try calling this before your request
$.ajaxSetup({async: false});
This will make the getScript method synchronous
When using something like getSript, it's important to remember that it is fetching asynchronously. Meaning, the browser fires off the request and while that's happening, code after that line executes without pause.
jQuery provides a callback function parameter to get script that allows you to do something after the asynchronous fetch is finished.
Try this:
var script = function(){
var scripts = {};
return {
load: function(jsurl, callback){
$.getScript(jsurl, callback);
},
run: function(js){
window[js].apply(this,null);
}
}
};
Then, when using it:
ctlr.load( 'remote.js', function(){
// remote script successfully loaded
ctlr.run( 'remotefunc' );
});
Could this be a timing issue?
In your working example you call the function in a callback which jQuery will not invoke until the script is loaded. In your non-working example, you call the function immediately after getScript which is asynchronously loading the script.

Extending every Ajax.Request onSuccess event (Javascript Prototype Framework)

I have an application that uses Ajax.Request and its onSuccess event handler in lots of places.
I need to call a function (that will check the response) before all these onSuccess events fire. I tried using Ajax.Responders.register with onComplete event but it fires after Ajax.Request's onSuccess event. Any suggestions?
similar to Aleksander Krzywinski's answer, but I believe this would prevent you from having to sprinkle the use of "wrap" everywhere, by consolidating it to the onCreate Responder.
Ajax.Responders.register({
onCreate: function(request) {
request.options['onSuccess'] = request.options['onSuccess'].wrap(validateResponse);
}
});
There are several events to chose from. Here is the event chain for Ajax.Request:
onCreate
onUninitialized
onLoading
onLoaded
onInteractive
onXYZ, onSuccess or onFailure
onComplete
onLoading, onLoaded, onInteractive sound interesting, but according to the spec they are not guaranteed to happen. That leaves you with the possibility to hook on to onCreate, which is called just after the request object is build, but before the request is actually made.
This might be a little late, but for the benefit of anyone else wondering about the same problem I will propose this solution:
You can use Prototypes own implementation of aspect-oriented programming to do this. Granted you will have to modify all your onSuccess-parameters, but it can be done with a simple search-and-replace, instead of updating all your callback functions. Here is an example Ajax.Request creation:
new Ajax.Request('example.html', {
parameters: {action: 'update'},
onSuccess: this.updateSuccessful
});
Say you have similar code snippets spread all over your code, and you want to preceed them all with a certain function that validates the response before the actual function is run(or even prevented from running at all). By using Funtion.wrap supplied in Prototype we can do this by extending the code above:
new Ajax.Request('example.html', {
parameters: {action: 'update'},
onSuccess: this.updateSuccessful.wrap(validateResponse)
});
Where 'validateResponse' is a function similar to this:
// If you use the X-JSON-header of the response for JSON, add the third param
function validateResponse(originalFn, transport /*, json */) {
// Validate the transport
if (someConditionMet) {
originalFn(transport /*, json */);
}
}
Thus you have extended your onSuccess-functions in one central place with just a quick search for onSuccess and pasting in 'wrap(validateResponse)'. This also gives you the option of having several wrapper-functions depending on the needs of the particular Ajax-request.
You can run your method before the other code in onSuccess and return false if something is wrong.
Don't know if this is the cleanest solution, but for me it did the trick.
var tmp = Ajax.Request;
Ajax.Request = function(url, args) {
// stuff to do before the request is sent
var c = Object.clone(args);
c.onSuccess = function(transport){
// stuff to do when request is successful, before the callback
args.onSuccess(transport);
}
var a = new tmp(url, c);
return a;
}
Ajax.Request.protoype = new tmp();
Ajax.Request.Events = tmp.Events;
delete tmp;
"General solution" - independent upon JS framework (kind of)
var oldFunc = Ajax.Request.onSuccess;
Ajax.Request.onSuccess = function foo() {
alert('t');
oldFunc.apply(this, arguments);
}
This will "extend" your JS function making it do exactly what it used to do except show an alert box every time before it executes...

Categories