I have a simple greasemonkey script that makes some simple dom manipulation. The greasemonkey script is loaded after the DOM is loaded, that's fine so fare and it does work for the initial Page. But on this site (it's twitter ;-) ) parts of the page get loaded after a click by xmlhttprequest and this part did not get manipulated by my greasemonkeyscript.
Is there a simple possibility to run the script again after the xmlhttprequest is loaded or after the DOM is changed by the request?
A powerful technique when using Greasemonkey is to 'hijack' existing JavaScript functions. If the page you are altering has a function called processAjaxResponse which is called when to process the XmlHttpRequest response, the you can do the following in your Greasemonkey script:
var originalProcessAjaxResponse;
function myNewProcessAjaxResponse() {
/* Manipulate DOM if you need to, and then... */
originalProcessAjaxResponse();
}
function hijack() {
var originalProcessAjaxResponse = processAjaxResponse;
processAjaxResponse = myNewProcessAjaxResponse();
}
This allows you to inject your own functionality when the AJAX response event occurs.
Thanks Howard. It was the way in the right direction.
It's quite hard to find the right entry point to hijack a function on a minifyied script. But I found that there is a huck in the twitter script. It does call window.onPageChange after the Ajax request. (I wonder if this is a common best practice in javascript and if other do this as well?) I did found some code on http://userscripts.org/scripts/review/47998 wich does use this posibility to attach events.
if (typeof unsafeWindow.onPageChange === 'function') {
var _onPageChange = unsafeWindow.onPageChange;
unsafeWindow.onPageChange = function(){
_onPageChange();
filter();
};
} else {
unsafeWindow.onPageChange = filter;
}
Related
We are attempting to add CSRF protection to our existing java web application by using CSRFGuard. We've followed the OWASP's guide to token injection, and that has gotten us most of the way there. We're using the dynamic DOM manipulation method, and find that most URLS/forms/AJAX calls are properly formatted with the inserted CSRF token. Our issue is this:
Parts of some pages are generated dynamically by AJAX calls that return jspfs. The jspfs that are returned have links which were never subject to the CSRFGuard DOM Manipulation, and as such, don't have the CSRF token. Clicking on those links causes a CSRF violation because no token is present.
Furthermore, according to the OWASP guide for AJAX support, the dynamic script needs to be reference prior to the AJAX call so that the AJAX call can be intercepted and have the CSRF token inserted into the header. This is the same script that dynamically updates the DOM. So - to solve the issue posed in this question I would need to run the script after the AJAX call, but I also need to run it before the AJAX call to make it in the first place. Trying to run it twice causes issues.
What is the proper solution here? Does that CSRFGuard javascript file need to be modified so that dynamic token injection can be run against targeted elements? Has this issue been solved already?
I had the same problem. I modified csrfguard.js this way:
I moved out all functions from (function() {}) block and put them before the block.
I defined 2 new functions
function getTokenNameValuePair() {
var xhr = window.XMLHttpRequest ? new window.XMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
var csrfToken = {};
xhr.open("POST", "%SERVLET_PATH%", false);
xhr.setRequestHeader("FETCH-CSRF-TOKEN", "1");
xhr.send(null);
return xhr.responseText;
}
function injectTokensForNewTags() {
var token_pair = getTokenNameValuePair();
token_pair = token_pair.split(":");
var token_name = token_pair[0];
var token_value = token_pair[1];
injectTokens(token_name, token_value);
}
And your AJAX that is returning a HTML chunk with links should look like this:
$.post(loadurl, function(data) {
$(target).html(data);
injectTokensForNewTags();
});
Note: this answer requires modifying CSRFGuard.
I am using SkateJS (https://github.com/skatejs/skatejs) to watch for dynamic updates of <a>, <img>, and <form> tags and calling a routine I added to csrfguard.js to add the token to them.
This catches tags that are inserted after the initial DOM load by javascript toolkits.
I had to also modify the script to keep it from always scanning the entire DOM tree at load time to insert the tokens. This was very inefficient and was unnecessary with the above method.
Here's an example of the SkateJS config:
window.addEventListener('load',
function() {
var config = {
ready : function(element) {
if (CsrfGuard && CsrfGuard.isEnabled) {
CsrfGuard.injectTokens([element]);
} else {
skate.destroy();
}
},
type : skate.types.TAG
};
skate('a', config);
skate('img', config);
skate('form', config);
}
);
Note: this will not work on IE8. For that, I am using Microsoft DHTML behaviors.
<![if IE 8]>
<style>
a,img,form {
behavior: url("csrf_ie8.htc");
}
</style>
<![endif]>
csrf_ie8.htc:
<public:attach event="ondocumentready" onevent="CsrfGuard.injectTokens([element]);" />
I want to develop a Chrome extension, just imagine when Facebook loads you are allowed to add extra JS on it.
But my problem is I can't modify the DOM of the later content, which means the newly loaded content that appear when the user scrolled down.
So I want to detect XHR using JavaScript.
I tried
send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
/* Wrap onreadystaechange callback */
var callback = this.onreadystatechange;
this.onreadystatechange = function() {
if (this.readyState == 4) {
/* We are in response; do something, like logging or anything you want */
alert('test');
}
callback.apply(this, arguments);
}
_send.apply(this, arguments);
}
But this is not working.. any ideas?
Besides Arun's correct remark that you should use _send for both, your approach doesn't work because of how Content Scripts work.
The code running in the content script works in an isolated environment, to prevent it from conflicting with page's own code. So it's not like you described - you're not simply adding JS to the page, you have it run isolated. As a result, your XHR replacement only affects XHR calls from your extension's content scripts and not the page.
It's possible to inject the code into the page itself. This will affect XHR's from the page, but might not work on all pages, if the Content Security Policy of the page in question disallows inline code. It seems like Facebook's CSP would allow this. Page's CSP should not be a problem according to the docs. So, this approach should work, see the question I linked.
That said, you're not specifically looking for AJAX calls, you're looking for new elements being inserted in the DOM. You can detect that without modifying the page's code, using DOM MutationObservers.
See this answer for more information.
to detect AJAX calls on a webpage you have to inject the code directly in that page and then call the .ajaxStart or .ajaxSuccess
Example:
// To Successfully Intercept AJAX calls, we had to embed the script directly in the Notifications page
var injectedCode = '(' + function() {
$('body').ajaxSuccess(function(evt, request, settings) {
if (evt.delegateTarget.baseURI == 'URL to check against if you want') {
// do your stuff
}
});
} + ')();';
// Inserting the script into the page
var script = document.createElement('script');
script.textContent = injectedCode;
(document.head || document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
I need to cache some <script src> that I receive via AJAX. Currently each call try to load the src via AJAX, as default. But the problem is that this script never change in a session and I need only re-eval this on document.
To be more clear, take this example of AJAX content result:
<strong>Hello World!</strong>
<script src="hello-world.js"></script>
If I call this AJAX three times, the hello-world.js is called three times too, but I need only re-execute this, without try to download it again. Browser cache help a lot, but I really do not can download it again every time.
I like to set some data to script, to jQuery know that I want only re-execute it, instead of download again. Like:
<script src="hello-world.js" data-cache="true"></script>
Any solution?
If think about a good solution for my case... I just replaced the src with data-src, so jQuery will not get the content automatically, so I have time to work with my content and find data-src and create my own cache system. Works fine to me.
You can check my code here:
// Cache system (outside of AJAX engine)
var script_cache = {};
// Inside of [jQuery.ajax].success method
// where "data_html" is my jQuery(data_html) AJAX response.
// Find all script with data-src
jQuery('script[data-src]', data_html).each(function() {
var self = jQuery(this),
self_src = self.data('src');
// If data was loaded before, so just execute it again
if(typeof script_cache[self_src] !== "undefined") {
jQuery.globalEval(script_cache[self_src]);
}
// Else, will load, cache and execute now
// Note that we download with dataType text
else {
jQuery.ajax(self_src, {
dataType: "text"
}).success(function(data) {
script_cache[self_src] = data;
jQuery.globalEval(data);
});
}
// Finally we remove the node, only to avoid problem
self.remove();
});
Alternative solutions are welcome.
I need to implement a cross-site comet http server push mechanism using script tag long polling. (phew...) For this, I dynamically insert script tags into the DOM and the server sends back short js scripts that simply call a local callback function that processes the incoming messages. I am trying to figure out a way to associate each one of these callback calls with the script tag that sent it, to match incoming replies with their corresponding requests.
Clearly, I could simply include a request ID in the GET url, which is then returned back in the js script that the server generates, but this creates a bunch of unnecessary traffic and doesn't strike me as particularly elegant or clever.
What I would like to do is to somehow associate the request ID with the script tag that I generate and then read out this request ID from within the callback function that is called from inside this script tag. That way, all the request management would remain on the client.
This leads me to the following question: Is there a way to ask the browser for the DOM element of the currently executing script tag, so I can use the tag element to pass arguments to the contained javascript?
I found this thread:
Getting the currently executing, dynamically appended, script tag
Which is asking exactly this question, but the accepted answer isn't useful to me since it still requires bloat in the server-returned js script (setting marker-variables inside the script) and it relies on unique filenames for the scripts, which I don't have.
Also, this thread is related:
How may I reference the script tag that loaded the currently-executing script?
And, among other things, suggests to simply grab the last script in the DOM, as they are executed in order. But this seems to only work while the page is loading and not in a scenario where scripts are added dynamically and may complete loading in an order that is independent of their insertion.
Any thoughts?
PS: I am looking for a client-only solution, i.e. no request IDs or unique callback function names or other non-payload data that needs to get sent to and handled by the server. I would like for the server to (theoretically) be able to return two 100% identical scripts and the client still being able to associate them correctly.
I know you would like to avoid discussions about changing the approach, but that's really what you need to do.
First, each of the script tags being added to the DOM to fire off the poll request is disposable, i.e. each needs to be removed from the DOM as soon as its purpose has been served. Else you end up flooding your client DOM with hundreds or more dead script tags.
A good comparable example of how this works is jsonp implementations. You create a client-side named function, create your script tag to make the remote request, and pass the function name in the request. The response script wraps the json object in a function call with the name, which then executes the function on return and passes the json payload into your function. After execution, the client-side function is then deleted. jQuery does this by creating randomly generated names (they exist in the global context, which is really the only way this process works), and then deletes the callback function when its done.
In regards to long polling, its a very similar process. Inherently, there is no need for the response function call to know, nor care, about what script tag initiated it.
Lets look at an example script:
window.callback = function(obj){
console.log(obj);
}
setInterval(function(){
var remote = document.createElement('script');
remote.src = 'http://jsonip.com/callback';
remote.addEventListener('load', function(){
remote.parentNode.removeChild(remote);
},false);
document.querySelector('head').appendChild(remote);
}, 2000);
This script keeps no references to the script elements because again, they are disposable. As soon as their jobs are done, they are summarily shot.
The example can be slightly modified to not use a setInterval, in which case you would replace setInterval with a named function and add logic into the remote load event to trigger the function when the load event completes. That way, the timing between script tag events depends on the response time of your server and is much closer to the actual long polling process.
You can extend this even further by using a queueing system to manage your callbacks. This could be useful if you have different functions to respond to different kinds of data coming back.
Alternatively, and probably better, is to have login in your callback function that handles the data returned from each poll and executes whatever other specific client-side logic at that point. This also means you only need 1 callback function and can get away from creating randomly generated callback names.
If you need more assistance with this, leave a comment with any specific questions and I can go into more detail.
It's most definitely possible but you need a little trick. It's a common technique known as JSONP.
In JavaScript:
var get_a_unique_name = (function () {
var counter = 0;
return function () {
counter += 1;
return "function_" + counter;
}
}()); // no magic, just a closure
var script = document.createElement("script");
var callback_name = get_a_unique_name();
script.src = "/request.php?id=12345&callback_name=" + callback_name;
// register the callback function globally
window[callback_name] = function (the_data) {
console.log(the_data);
handle_data(the_data); // implement this function
};
// add the script
document.head.appendChild(script);
The serverside you can have:
$callback_name = $_GET["callback_name"];
$the_data = handle_request($_GET["id"]); // implement handle_request
echo $callback_name . "(" . json_encode($the_data) . ");";
exit; // done
The script that is returened by /request.php?id=12345&callback_name=XXX will look something like this:
function_0({ "hello": "world", "foo" : "bar" });
There may be a solution using onload/onreadystate events on the script. I can pass these events a closure function that carries my request ID. Then, the callback function doesn't handle the server reply immediately but instead stores it in a global variable. The onload/onreadystate handler then picks up the last stored reply and tags it with the request ID it knows and then processes the reply.
For this to work, I need to be able to rely on the order of events. If onload is always executed right after the corresponding script tag finishes execution, this will work beautifully. But, if I have two tags loading simultaneously and they return at the same time and there is a chance that the browser will execute both and afterwards execute botth onload/onreadystate events, then I will loose one reply this way.
Does anyone have any insight on this?
.
Here's some code to demonstrate this:
function loadScript(url, requestID) {
var script = document.createElement('script');
script.setAttribute("src", url);
script.setAttribute("type", "text/javascript");
script.setAttribute("language", "javascript");
script.onerror = script.onload = function() {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || script.readyState == 'complete') {
script.onerror = script.onload = script.onreadystatechange = function () {}
document.body.removeChild(script);
completeRequest(requestID);
}
}
document.body.appendChild(script);
}
var lastReply;
function myCallback(reply) {
lastReply = reply;
}
function completeRequest(requestID) {
processReply(requestID, lastReply);
}
function processReply(requestID, reply) {
// Do something
}
Now, the server simply returns scripts of the form
myCallback(message);
and doesn't need to worry at all about request IDs and such and can always use the same callback function.
The question is: If I have two scripts returning "simultaneously" is it possible that this leads to the following calling order:
myCallback(message1);
myCallback(message2);
completeRequest(requestID1);
completeRequest(requestID2);
If so, I would loose the actual reply to request 1 and wrongly associate the reply to request 2 with request 1.
It should be quite simple. There is only one script element for each server "connection", and it can easily be stored in a scoped, static variable.
function connect(nameOfCallback, eventCallback) {
var script;
window[nameOfCallback] = function() { // this is what the response invokes
reload();
eventCallback.call(null, arguments);
};
reload();
function reload() {
if (script && script.parentNode)
script.parentNode.removeChild(script);
script = document.createElement(script);
script.src = "…";
script.type = "text/javascript";
document.head.appendChild(script);
// you might use additional error handling, e.g. something like
// script.onerror = reload;
// but I guess you get the concept
}
}
My web app dynamically loads sections of its UI with jquery.ajax. The new UI sections come with script though. I'm loading them as such:
Use...
$.ajax({
url: url,
dataType: 'html',
success: function(data, textStatus, XMLHttpRequest) {
$(target_selector).html( data );
update_ui_after_load();
}
});
This almost works. The problem is that the scripts included in the dynamic part of the page run before the new page fragment is inserted into the DOM. But often these scripts want to modify the HTML they're being delivered with. My best hacky solution so far is just to delay the scripts some reasonable amount of time to let the DOM insertion happen, by wrapping them in a setTimeout:
window.setTimeout( function() {
// process downloaded Fragment
}, 300);
Obviously this is unreliable and hideous. What's a better way?
Using
$(function);
will make the function you pass to jQuery to be run after the fragment is inline on the page.
I found it in
ASP.NET Ajax partial postback and jQuery problem
after looking at your question.
Are you familiar with the live() function? Might be what you're looking for here.
http://api.jquery.com/live/
The problem is that the scripts included in the dynamic part of the page run before the new page fragment is inserted into the DOM. But often these scripts want to modify the HTML they're being delivered with.
I'm fairly sure that in that case, the only sensible thing is to place the script after the HTML element.
Everything else would become kludgy quickly - I guess you could implement your own "ready" handler that gets executed after your HTML has been inserted, but that would be a lot of work to implement for no real gain.
I solved it by making a new simple ready handler system as follows...
var ajaxOnLoad = (function() {
var ajaxOnLoad = {};
var onLoadQueue=[];
ajaxOnLoad.onLoad= function(fn) {
onLoadQueue.push(fn);
}
ajaxOnLoad.fireOnLoad = function() {
while( onLoadQueue.length > 0 ) {
var fn = onLoadQueue.shift();
fn();
}
}
window.ajaxOnLoad = ajaxOnLoad;
return ajaxOnLoad;
})();
So in the pages which get .ajax() loaded, the scripts are queued to run with
ajaxOnLoad.onLoad( function() {
// Stuff to do after the page fragment is inserted in the main DOM
});
and in the code which does the insertion, before the update_ui_after_load() call, run
ajaxOnLoad.fireOnLoad();
A more complete solution could parse the pages, find script tags, and queue them up automatically. But since I have complete control of the fragments being inserted, it's easier for me to switch to using ajaxOnLoad.onLoad.