Tuesday, December 30, 2008

Getting available screen space for web pages

One of the most common requirements when creating a web page is to get the
space available in the browser window where the content will be displayed. It is always preferable to fit the content into the available space to prevent scroll bars and reduce the effort required on the part of the user. A number of factors cause variations in
the width and the height available for the content of the web page, some of them
being,

  1. Windows Taskbar
  2. Status bar
  3. Toolbars (for eg., MSN toolbar, Google toolbar etc)

Because the user can have any combination of these things enabled, it is difficult to get the exact space and height available. Add to this the fact that different browsers support different sets of properties of the BOM and even for the common set of properties, they give different meanings. Hence, we need to combine multiple approaches to get the available space so that it works in almost all browsers. The code that will do exactly this is given below :

//Returns an array containing the available width and height
function GetWindowSize()
{
....try
....{
........var myWidth = 0, myHeight = 0;
........if (typeof (window.innerWidth) == 'number')
........{
............//Non-IE
............myWidth = window.innerWidth;
............myHeight = window.innerHeight;
........}
........else if(document.documentElement && (document.documentElement.clientWidth
document.documentElement.clientHeight))
........{
............//IE 6+ in 'standards compliant mode'
............myWidth = document.documentElement.clientWidth;
............myHeight = document.documentElement.clientHeight;
........}
........else if (document.body && (document.body.clientWidth
document.body.clientHeight))
........{
............//IE 4 compatible
............myWidth = document.body.clientWidth;
............myHeight = document.body.clientHeight;
........}
........return new Array(myWidth,myHeight);
....}
....catch (ex)
....{
........return null;
....}
}

The above code should suffice for almost all browsers and different browser versions. It is not advisable to use "Screen.availWidth" and "Screen.availHeight" since it doesn't return the correct values in case the user is using toolbars. I have tested the above code for IE and FireFox with and without toolbars and it worked like a charm.

Monday, December 29, 2008

Preventing JavaScript injection attacks

Whenever one is developing web pages that accept input from the user, one needs to be extra careful in order to prevent the extremely dangerous XSS (Cross Site Scripting) attacks. Typically, these attacks are carried out by users who input malicious scripts in the input fields provided on the web page. The scripts get executed when the page tries to display back the values that were input by the user. For example, if the user is presented with a field for entering his name, he may enter something like

ABC;<script>alert("This site is hacked")</script>

Now, whenever the site displays this user's name, the script also gets executed and the viewer of the page will get an alert box proclaiming "This site is hacked". Although this is a contrived example, there is the potential to do much more harmful things using the same technique. Skilled users can write scripts that can read data from cookies, for example, and send to another site (hence the name cross site scripting).

In order to prevent such things from happening, all one has to do is "Server.HTMLEncode" all the values that the user provides, assuming you are using ASP.NET. There are 2 places where you can do the encoding :

  1. You can do the encoding while storing the data in the database so that anytime those values are to be displayed to the user, they won't cause any problems.
  2. You can also do the encoding just before showing the values to the user. This technique can be used when you are just displaying the values without ever storing them in the database or in case you need to display the values before storing them in the database

What Server.HTMLEncode does is it converts all the special characters such as "<" and ">" to their ASCII equivalents so that the browser runtime engine interprets them as normal characters instead of interpreting and executing them. You can find the complete list of actions taken by this method here.

Remember to encode any and all values you accept from users to avoid having serious XSS attacks launched on your website.

Sunday, December 28, 2008

Weird behaviour of "onchange" event of Listbox in FireFox

The HTML listbox has an "onchange" event which is fired whenever the item selected in the listbox has been changed. The contents of the listbox can be changed either by clicking the corresponding item from the listbox or by using the up and down arrow keys or pressing the beginning alphabet of any of the items when the listbox has focus. Ideally, even when changing the selected item by using the keyboard, the onchange event should be fired. In fact, the event is fired in all major browsers except Firefox. With Firefox, the onchange event is called only when the listbox loses focus.

Suppose you have a form whose contents you would like to change based on the value selected in the listbox. For example, have different fields on the form based on whether the user is interested in Sports, Music, Dance etc. You can do this by capturing the item selected by the user in the onchange event and making the necessary changes by manipulating the DOM. However, you would have a problem with users using FireFox. To be able to capture the value every time the user makes a new selection, we need to add some javascript for the "onkeyup" event. The code needed is,

<select id="items" onchange='itemSelected();' onkeyup="this.blur();this.focus();">
</select>

All that needs to be done is to momentarily remove focus from the listbox so that the onchange event is called and then, set the focus again. Setting the focus again on the listbox is needed because, if we don't do that, the user will have the set the focus explicitly on the listbox every time he changes it's value with the keyboard - he wouldn't appreciate this if he wants to select a value which is 4-5 values further in the list from where he's currently at.

I spent nearly 2 hours trying to figure out why the onchange event wasn't getting called in FireFox. Hope this post will go some way in helping you reduce the debugging time in case you happen to come across a similar situation.

Sunday, December 7, 2008

Calling Web Services from Javascript

One of the most powerful features of ASP.Net AJAX is the ability to call server side web services seamlessly from plain Javascript. There is nothing special that needs to be done to create a web service that can be called from JavaScript apart from adding the "ScriptService" attribute at the beginning of the web service declaration. We can use ASMX as well as WCF web services with ASP.Net AJAX. Let's take a look at the steps needed to call web services with AJAX.
  • Server side

On the server side, we will create a normal ASMX web service.

  1. Right click your project in Visual Studio and click "Add new item". In the dialog that opens up, select "Web service" and give it an appropriate name
  2. Once the web service has been added, open it's .cs file. At the very top, before the class declaration there will be the following commented attribute
  3. [System.Web.Script.Services.ScriptService]

    Uncomment this attribute.

  4. Add the methods you want to call from the client side and annotate them with the [WebMethod] attribute
  5. Test the web service and ensure that the web methods are working fine
  • Client side

On the client side, we will need to do the following to be able to call the web service created in the above steps

  1. Add a ScriptManager tag at the top of your page. The script manager is required on every page that should provide AJAX functionality. It instantiates the PageRequestManager class and handles the downloading of all the necessary script and service proxy files. (If you are using master pages and the ScriptManager is present in the master page, you can add a ScriptManagerProxy tag to the current page)
  2. Within the ScriptManager tag, add a ServiceReference and provide the path to the web service. It should look something like this,
  3. <services>
    <asp:servicereference path="~/Sample.asmx">
    </services>
    </asp:scriptmanager>

    Here, I am adding a reference to a web service named "Sample.asmx" which is present in the same project. What happens under the hood is that, for each service that you reference with the ServiceReference tag, a proxy is created and downloaded on the client side and this proxy is used to validate the calls from JavaScript to the web service.

  4. Once this is done, we can call a web method from JavaScript as follows :
  5. WebServiceClass.WebMethod(parameters, Success Callback function, Failure callback function, context);

    parameters : We can specify as many as needed, or even 0, parameters to be passed to the web method, each separated from the other by comma

    Success Callback function : We specify name of the JavaScript function that will be called when the web method completes execution

    Failure Callback function : We specify name of the JavaScript function which will be called in case the web method encountered some error during execution

    context : This is an optional string value that we can provide. It is used in case we are using the same JavaScript function as callback for multiple web methods so that in the JavaScript function, we can determine the web method call for which the callback was invoked.

  6. The final step is to code the success and failure callbacks, syntax for which is as follows :

function SuccessCallback(response, context, method)

function FailureCallback(error, context, method)

Here,

response : It contains the values returned by the web method. It can be as simple as an integer or a string or as complex as JSON representation of List or even a custom object. The values can be accessed using the normal object.property syntax.

error : Object representing the error that occurred in the web method. The most useful method of this object is get_message() which returns a description of the error that occurred

context : Optional value, which will have the same value as provided as the last parameter when calling the web method

method : Name of the web method from which the current JavaScript function has been invoked

Having the ability to call web services asynchronously from the client provides tremendous flexibility as well as performance advantages to the programmers and allows us to create really powerful and performant sites with ease.

Ignoring certain characters from user input in HTML Textbox

One common requirement when creating user input forms in websites is to not show some of the characters in a text box. For example, we would not want to show alphabets in input fields for dates and age. Similarly, we would not want to show numbers in an input field for name.

As it turns out, this is really easy to do with some simple JavaScript. We can accomplish this with the following steps :
  1. Identify the characters that need to be ignored (Black listing approach)
  2. Add an event handler for the key down event on the text box
  3. In the event handler, check whether the input character is one of the characters that needs to be ignored. For these characters, just return false from the event handler. This will prevent that particular character from reflecting in the text box
  4. Optionally, one can show an error message if one of the unwanted characters is input. This is done to avoid the user from getting confused when his inputs don't cause any changes in the contents of the text box

Here is the code for declaring text field in HTML :

<input id="'startDate'" type="'text'">

We can add the event handler in the HTML tag itself or in a Javascript function which will be called on the "onload" event of the body as follows :

document.getElementById('startDate').onkeydown = CheckNumber;

Here, we are using the "Key Down" event of the text box to check for the character input by the user. Finally, the event handler function code is as follows :

function CheckNumber(e)
{
....try
....{
........if (!e)
........{
............e = window.event;
........}
........var keynum;
........try
........{
............//IE : Get the code of the key the user pressed
............keynum = e.keyCode;
........}
........catch (err)
........{
............//Netscape/Firefox/Opera : Get the code of the key the user pressed
............keynum = e.which;
........}

/*Prevent any character not in the range 0-9 and not '/', backspace, delete or left and right arrow keys, Tab and Shift+Tab. ASCII Code : / is 191, Backspace is 8,Delete is 46, Left : 37, Right :39, Tab : 9, Shift + Tab: 16*/

........if (keynum != 191 && (keynum <> 57) && keynum != 8 && keynum != 46 && keynum != 37 && keynum != 39 && keynum != 9 && keynum != 16)
........{
............//Show error to the user to inform him that the input was invalid
............document.getElementById("dateErrorMessage").style.display = "inline";
............//Return false to prevent the character from being added to textbox
............return false;
........}
........else
........{
............//Remove error message if it's visible
............document.getElementById("dateErrorMessage").style.display = "none";
........}
....}
....catch (ex)
....{
........alert("Check number : " + ex.message);
....}
}

As can be seen from the comments, we first retrieve the ASCII code for the character typed by the user (as expected, we have 2 different ways of getting the value based on the browser type) . Then we check against the characters we will allow (I am using white listing here, one can use blacklisting also, as mentioned earlier. The right approach will depend on the number of conditions that need to be specified but generally white listing is preferred for 2 reasons. It will cause fewer problems in case you miss out on some conditions and this mistake will be easier to catch during testing. If the character doesn't fall within our white list, we show an error message on a label and then return false to prevent it from appearing in the text box. Otherwise, we do nothing so that the character appears normally in the text box.

So there you have it, a texbox that accepts restricted inputs. Customise for the type of inputs you want to allow/disallow.