Hide Focus attribute and CSS

This is an odd MSIE proprietary attribute (‘hidefocus‘) that is used to remove the dotted border (outline) of the focused elements, usually links or images, in the page. Modern CSS is much more robust and should be used in most cases for valid HTML.

The example below requires you to add the class specifically to elements, but your design may allow for you to do so for as elements of larger components of your design.

<style type="text/css">
.hidefocus:focus {
outline: none;
}
</style>

<a href="#" class="hidefocus" hidefocus="true">Some link</a>

NOTE: You should still provide some sort of visual focus mechanism for accessibility purposes, even when you prevent the default behavior.

REFERENCES:

MSIE7+ image resizing interpolation

MSIE7 and later by default use an image resizing algorithm that means that scaled down images can look blocky and rasterized. To solve this and make them smoother, we simply enable a much better resizing algorithm that is available in MSIE that produces results similar to what you’d expect from most image editing software.


/* bicubic resizing for non-native sized IMG */
<style type="text/css">
img { -ms-interpolation-mode: bicubic; }
</style>

MSIE Table styling of empty cells

This one had me stumped for some time, when a table contained either a <td></td> or <th></th> without content the cell would not be properly styled, often lacking borders. Traditionally the solution I used was to insure that all table cells had at least a single space within them so that they would be styled properly in MSIE.

There’s a simpler solution, the below CSS changes the default behavior so that the traditional ‘hack’ is no longer required:


table {
border-collapse:collapse;
empty-cells:show;
}

REFERENCES:

META Tag ‘MSThemeCompatible’

Okay, so this one’s a little old, and I just found it while looking at some of Microsoft Update’s HTML source, it appears to be relevant for MSIE6 and newer and may be responsible for some interesting styling and behaviour of form components.

A quick search for it turns up lots of discussions about other browers such as Firefox being effected if the value is not defined… as such it’s likely a good idea to define it in your pages to be sure.

An old MSDN entry reads…

When running on Windows XP, Internet Explorer 6 and the content displayed in it sports a look and feel that matches the Windows XP platform. You can opt to have your HTML content not take on the same look as the operating system for elements such as buttons and scroll bars, by adding the following META tag:
<meta http-equiv="MSThemeCompatible" content="no" />

Setting this will disable theme support for the document. Some background on this, Windows XP (MSIE6) allows for the use of themes for the operating system to change the general color scheme of many elements.
As such, many HTML components (such as SELECT dropdowns, BUTTONS and INPUT fields ‘MAY’ also be effected if you don’t explicitly prevent it in your code.

There was some support for this in Mozilla Firefox builds for Windows, as such, while I’d normally recommend using a conditional comment, I’m torn in this case.

Cheers

MSIE browser testing

Testing web applications with various versions of MSIE (Internet Explorer) often proves problematic as it’s generally not possible to have more than one version installed on a single Windows installation. There are various approaches, such as:

  • Having an entire test lab with different browser configurations.
  • More often, it’s also possible to run a set of virtual machines on a single desktop.
  • Another option is to find a package that ‘hacks’ around some of the Windows limitations and installs (at least partially) the browser rendering capabilities.

NOTE: the final method above has some quirks, but you can generally use it for preliminary testing by developers as it’s obviously easier to maintain.

Here are a few common packages that I’m aware of:

Happy testing!

JavaScript StringBuffer

Eventually, you come to a point where the performance of JavaScript becomes an issue.  Most modern browsers have made significant improvements  in their javascript engines, unfortunately Microsoft has yet to do the same.  MSIE’s (at least up to and including MSIE8)  javascript performance lags far behind Mozilla/Firefox, Safari, Chrome and Opera.

String concatenation is extremely slow in MSIE,  but arrays are generally fast… as such it’s often beneficial to implement an object similar to the Java StringBuffer (or StringBuilder) for JavaScript.

The StringBuffer implementation… you can customize to make it more functional, but this is the core:

function StringBuffer(){
this.buffer = [];
}
StringBuffer.prototype.append = function append(string){
this.buffer.push(string);
return this;
};
StringBuffer.prototype.toString = function toString(){
return this.buffer.join(“”);
};

Example:

function example_slow(x1,x2,x3){
var rc = x1;
rc+=x2;
rc+=x3;
return rc;
}

function example_fast(x1,x2,x3){
var sb = new StringBuffer();
sb.append(x1);
sb.append(x2);
sb.append(x3);
return sb.toString();
}

References:

Cheers

Browser Rendering Engines

This is knowledge that is generally “tribal” by nature, reserved to only the nerdiest web developers, recently I was asked to name these and failed. Here’s the bounty of my research.

Gecko is generally considered to be the second most-popular layout engine on the Web, after Trident (used by Internet Explorer for Windows since version 4), and followed by WebCore (used by Safari) and Presto (used by Opera).

Gecko is the open source, free software web browser layout engine used in all Mozilla-branded software and its derivatives, including later Netscape browser releases. Written in C++ and licensed under MPL/GPL/LGPL triple license, Gecko is designed to support open Internet standards. Originally created by Netscape Communications Corporation, its development is now overseen by the Mozilla Foundation.

Trident (also known as MSHTML) is the name of the layout engine for the Microsoft Windows version of Internet Explorer. It was first introduced with the release of Internet Explorer version 4 in October 1997, has been steadily upgraded and remains in use today. For version 7 of Internet Explorer, Microsoft made significant changes to the Trident layout engine to improve compliance with web standards and add support for new technologies. Despite these changes, Trident remains significantly less compliant than competing layout engines Gecko, Presto and WebCore.

Presto is the name of the current (Opera 9 series) layout engine for the Opera web browser developed by Opera Software. It was first released (following several public betas and technical previews) on January 28, 2003 in Opera 7.0 for Windows. Presto replaced the Elektra engine used in versions 4–6 of Opera. Presto differs from Elektra in that it is dynamic: the page or parts of it can be re-rendered in response to DOM and script events. The Presto layout engine is only available as a part of Opera browser or related products. The source or binary (DLL) forms of the engine are not publicly available. Subsequent releases have seen a number of bugs fixed and optimizations to improve the speed of the ECMAScript (“JavaScript“) engine.

Tasman is the name of the layout engine introduced with version 5 of Internet Explorer for Mac. Tasman was an attempt to improve support for web standards, as defined by the World Wide Web Consortium. At the time of its release, Tasman was seen as the layout engine with the best support for web standards such as HTML and CSS. Unfortunately, MSIE for Mac is no longer supported, but newer versions of Tasman are incorporated in some other current Microsoft products.

Cheers!

MSIE PNG Alpha Transparency

In usual form, MSIE doesn’t directly implement Alpha-Transparency on PNG images. Typically this feature is used to allow for anti-aliased gradients on images so that they can be used to support a variety of backgrounds.

There are a variety of solutions online for this problem, however I take issue with most, here’s why:

  • .htc files – this is a proprietary Microsoft solution, to add support on most web servers the MIME type must also be added.
  • filter: progid: – this too is utilizing a standard in Microsoft’s own particular way.

While neither of these is perfect, the ‘filter:’ is obviously the best of two evils. Surround it with the “Conditional If” comments (previously documented) and you’re at least safe for most other browsers.

Here’s my example code:

<!–[if gte IE 5.5000]>
<script type=”text/javascript”>
function correctPNG() // correctly handle PNG transparency in Win IE 5.5 or higher.
{
for(var i=0; i<document.images.length; i++)
{
var img = document.images[i]
var imgName = img.src.toUpperCase()
if (imgName.substring(imgName.length-3, imgName.length) == “PNG”)
{
var imgID = (img.id) ? “id='” + img.id + “‘ ” : “”;
var imgClass = (img.className) ? “class='” + img.className + “‘ ” : “”;
var imgTitle = (img.title) ? “title='” + img.title + “‘ ” : “title='” + img.alt + “‘ “;
var imgStyle = “display:inline-block;” + img.style.cssText;
if (img.align == “left”) imgStyle = “float:left;” + imgStyle;
if (img.align == “right”) imgStyle = “float:right;” + imgStyle;
if (img.parentElement.href) imgStyle = “cursor:hand;” + imgStyle;
var strNewHTML = “<span ” + imgID + imgClass + imgTitle
+ ” style=\”” + “width:” + img.width + “px;height:” + img.height + “px;” + imgStyle + “;”
+ “filter:progid:DXImageTransform.Microsoft.AlphaImageLoader”
+ “(src=\'” + img.src + “\’, sizingMethod=’scale’);\”></span>”;
img.outerHTML = strNewHTML;
i = i-1;
}
}
}
window.attachEvent(“onload”, correctPNG);
</script>
<![endif]–>

References:

Good luck out there!

A Flexible AJAX Framework

AJAX = Asyncronous (but not always), JavaScript, XML (Text or JSON). These technologies have been around almost as long as the Internet itself, but have only recently been used together to change the way that web applications are built.

XMLHTTPRequest is an API used mainly by browser-based Javascript applications to send and retrieve data from servers. It was developed originally by Microsoft for Exchange Server’s Outlook Web Access, but it has since been widely supported in browsers and is the heart of AJAX dynamic Web applications.

I’ve messed with many of the frameworks available online, and while they are very good, I’ve routinely found issues or features that they lack. Over time I’ve cobbled together my own framework, I fully expect this to change over time, but publish it here for your consideration!

The XSS methods (and “otherHost” variable) are provided as MSIE6 allows for Cross-site Scripting here. MSIE7 and Mozilla will indicate errors if connections are attempted to a ‘non-originating host’. I’ve exploited this vulnerability in a few applications, as such I provide the test for you too!

NOTE: The example response program is written in PHP, but should be easily ported to any other language.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>

<head>

<title>AJAX Example</title>

<script type=”text/javascript”>

var xbusy = true;

var otherHost = ‘http://www.giantgeek.com’; // DO NOT USE the ending slash!

var xhr = null;

/**

* Check for existance on DOM browsers (Mozilla, etc.)

* @return xhr

*/

function ajaxCheckDOM(){

var myxhr = null;

if(window.XMLHttpRequest) {

try {

myxhr = new XMLHttpRequest();

}

catch(e) {}

}

return myxhr;

}

/**

* Check for existance on Windows/MSIE (prior to MSIE7 which is now DOM)

* Evaluate using – new ActiveXObject(“Microsoft.XMLDOM”);

* @return xhr

*/

function ajaxCheckActiveX(){

var myxhr = null;

//if(window.ActiveXObject){

try {

myxhr = new ActiveXObject(“Msxml2.XMLHTTP”);

}

catch(e) {

try {

myxhr = new ActiveXObject(“Microsoft.XMLHTTP”);

}

catch(e) {}

}

//}

return myxhr;

}

/**

* This is a default response hook for AJAX, you SHOULD create your own as it’s only for DEMO

* @param obj – the clicked item

* @param customObj – user defined

*/

function ajaxResponseHook(obj,customObj){

var txt = xhr.responseText;// NOTE: xhr.responseXML is also valid

popStatus(‘AJAX Response value [‘ + txt + ‘|’ + customObj +’]’,”);

}

/**

* This is a common Response handler AJAX, you SHOULD NOT need to modify, use a ‘custom’ ajaxhook function to process the responses.

* @param obj – the clicked item

* @param ajaxhook Function (default will be used if undefined)

* @param customObj (optional – left for developer implementation, passed to the ajaxhook)

*/

function ajaxResponse(obj,ajaxhook,customObj){

var status=”;

if(xhr!=undefined){

var state=xhr.readyState;

if(state!=undefined){

if(state==4){

var code = xhr.status;

status = xhr.statusText;

if(code==200){

popStatus(‘AJAX Response Received.’,”);

if(ajaxhook==undefined){

ajaxhook = function(){ ajaxResponseHook(obj,customObj); }

}

ajaxhook(obj,customObj);

ajaxBusy(obj,false);

} else {

popStatus(‘AJAX Error ‘ + code + ‘ ‘ + status + ‘.’,”);

}

ajaxBusy(obj,false);

xhr=null; /* MSIE6 leak fix */

}

}

}

}

/**

* NOTE: MSIE6-7 supports XSS url’s (be careful!)

* @param obj – the clicked item

* @param url – the GET url params (FQDN)

* @param async (true or false) – false will LOCK browser until response – use cautiously!

* @param callback Function – allows for customized response handling

*/

function ajaxObj(obj,url,async,callback){

if(xbusy == true){

popStatus(‘AJAX BUSY, Please Retry.’,”);

} else {

if(callback==undefined){

callback = function(){ ajaxResponse(obj,hook,”); }

}

ajaxInit(obj,url,async,callback);

}

}

/**

* This is a Non-Caching implementation of ajaxObj() to show how you can avoid the MSIE caching issue.

* NOTE: You can use similar approaches to cache per page or session.

* @param obj – the clicked item

* @param url – the GET url params (FQDN)

* @param async (true or false) – false will LOCK browser until response – use cautiously!

* @param callback Function – allows for customized response handling

*/

function ajaxObjNoCache(obj,url,async,callback){

var cacheBuster=uniqueUrl(url);// damn MSIE!

ajaxObj(obj,cacheBuster,async,callback)

}

/**

* Initializes the AJAX operation

* @param obj – item clicked

* @param url – FQDN

* @param async (true/false) – for locking

* @param callback Function – provided for customization.

*/

function ajaxInit(obj,url,async,callback){

xhr=ajaxCheckDOM();

if(!xhr){

xhr=ajaxCheckActiveX();

}

if(xhr){

ajaxBusy(obj,true);

popStatus(‘AJAX Start.’,”);

xhr.onreadystatechange = callback; // method call

try {

xhr.open(‘GET’,url,async); /* POST */

//xhr.setRequestHeader(‘Content-Type’,’application/x-www-form-urlencoded’);

xhr.send(”); /* null */

}

catch(e)

{

if(xhr){

var code = xhr.status;

var status = xhr.statusText;

popStatus(‘AJAX Client SECURITY ‘ + navigator.appName +’ ‘+ navigator.appVersion + ‘ ‘ + code + ‘ ‘ + status + ‘.’,”);

}

}

ajaxBusy(obj,false);

} else {

popStatus(‘AJAX Client ERROR.’,”);

}

}

function ajaxBusy(obj,yn){

if(yn){

swapStyleObj(obj,’idle’,’busy’);

} else {

swapStyleObj(obj,’busy’,’idle’);

}

xbusyInd(yn,”);

}

function xmillis(){

return new Date().getTime();

}

function swapStyleObj(obj,oldCSS,newCSS){

if(obj!=undefined) {

var current=obj.className;

if(current!=undefined){

var txtOld = current.replace(newCSS,’ ‘);//no doubles

var txtMid = txtOld.replace(oldCSS, ‘ ‘);

var txtNew = (txtMid + ‘ ‘ + newCSS);

obj.className = txtNew;

} else {

obj.className = newCSS;

}

}

}

function xbusyInd(valnew, msg){

xbusy = valnew;

if(xbusy==true){

document.body.style.cursor=’wait’;

if(msg != ”){

top.window.defaultStatus=msg;

popStatus(msg,”);

}

showDiv(‘throbber’);

//blockScreen();

} else {

document.body.style.cursor=’default’;

hideDiv(‘throbber’);

//unBlockScreen();

}

}

function showDiv(id) { //show a div

var obj=xgetHelper(id);

if(obj!=null){ obj.style.display=”block”;}

//xrestart();

}

function hideDiv(id) { //hide a div

var obj=xgetHelper(id);

if(obj!=null){ obj.style.display=”none”;}

//xrestart();

}

function popStatus(txt,title){

popit(‘statusdyn’,’statusarrow’,’statusul’,’statusdiv’,txt,false,title,”);

}

function popit(dynId,arrowId,ulId,divId,txt,expand,title,cssCls){

popText(ulId,txt,title,cssCls);

showDiv(divId);

if(expand == true){

var arrowObj=xgetHelper(arrowId); if(arrowObj!=null){ arrowObj.style.backgroundPosition=’2px -106px’; }

showDiv(dynId);

}

}

function popText(id,txt,title,cssCls){

var obj = xgetHelper(id);

if(obj != null){

var oldHTML = obj.innerHTML;

var cls=”; if(cssCls!=”){ cls=’ class=”‘ + cls +'”‘; }

var htm = ‘<‘+’li’+ cls +’>’ + txt + ‘<‘+’/’+’li’+’>’ + oldHTML;

obj.innerHTML = htm;

}

}

function xgetHelper(id){

var obj = null;

try {

obj = document.getElementById(id);

} catch(z) {

var dummy=alert(“Error:” + z);

}

return obj;

}

function arrowTog(objectID,arrow) {

var obj = xgetHelper(objectID);

if(obj!=null){

if (obj.style.display ==’block’) {

arrow.style.backgroundPosition = ‘2px -21px’;}

else {arrow.style.backgroundPosition = ‘2px -106px’;}

objTog(objectID);

}

return false;

}

function objTog(objectID) {

var obj = xgetHelper(objectID);

if(obj!=null){

if (obj.style.display ==’block’) obj.style.display=’none’;

else {obj.style.display=’block’;}

}

return false;

}

function headTog(objectID,arrow) {

var obj = xgetHelper(objectID);

if(obj!=null){

if (obj.style.display ==’block’) {

arrow.style.borderBottomWidth = ‘1px’;}

else {arrow.style.borderBottomWidth = ‘0px’;}

arrowTog(objectID,arrow);

}

return false;

}

/*

* adds timestamp to URLs to make them unique

* @param URL String

*/

function uniqueUrl(x){

return urlAppender(x,’.cache’,xmillis());

}

/*

* helps to add parms to the url

* @param URL String

* @param aname String

* @param avalue String

*/

function urlAppender(x,aname,avalue){

var delim = “?”;

if(x.indexOf(“?”) >=0) { delim = “&”; }

return x + delim + aname + ‘=’ + avalue;

}

function xload(){

hideDiv(‘throbber’);

xbusy=false;

}

function testAjax(obj){

var callback=function(){ ajaxResponse(obj,null,’testAjax’); }

var x = ajaxObjNoCache(obj,’/ajax.php’,true,callback);

}

function testAjaxParms(obj){

var callback=function(){ ajaxResponse(obj,null,’testAjaxParms’); }

var x = ajaxObjNoCache(obj,’/ajax.php?testing=Y’,true,callback);

}

function testAjaxXSS(obj){

var callback=function(){ ajaxResponse(obj,null,’testAjaxXSS’); }

var x = ajaxObjNoCache(obj,otherHost+’/ajax.php’,true,callback,otherHost);

}

function testAjaxHook(obj){

var hook=function(){ customAjaxHook(obj,’ajaxhookTestMessageObect’); }

var callback=function(){ ajaxResponse(obj,hook,’testAjaxHook’); }

var x = ajaxObjNoCache(obj,’/ajax.php’,true,callback);

}

function testAjaxHookXSS(obj){

var hook=function(){ customAjaxHook(obj,’ajaxhookXSSMessageObect’); }

var callback=function(){ ajaxResponse(obj,hook,’testAjaxHookXSS’); }

var x = ajaxObjNoCache(obj,otherHost+’/ajax.php’,true,callback);

}

/**

* This is a custom implemenation, the customObj COULD be used for anything (perhaps delay measurement!)

* @param obj – the clicked item

* @param customObj – user defined

*/

function customAjaxHook(obj,customObj){

var xml = xhr.responseXML;// NOTE: xhr.responseText is also valid

var tmp = ‘DEMO customAjaxHook [‘ + customObj + ‘]\n’ + xml;

alert(tmp);

}

</script>

<style type=”text/css”>

.busy { color:red; }

.idle { color:black; }

</style>

</head>

<body onload=”xload();”><!– onbeforeunload=”alert(‘before’);” onunload=”alert(‘after’);” –>

<div id=”throbber”>WORKING!</div>

<div id=”statusdiv” class=”dyn” style=”display:none;”>

<h3><a id=”statusarrow” onclick=”headTog(‘statusdyn’,this);” href=”javascript:void(0);”>Status</a></h3>

<fieldset id=”statusdyn” style=”display:block;background-color:#ffffcc;”>

<div id=”statusBox” class=”box”>

<ul id=”statusul” class=”error”>

<li id=”ajaxStatus” style=”list-style:none;display:none;”>FILLER</li>

</ul>

</div>

</fieldset>

</div>

<h3><a href=”javascript:void(0);” onclick=”testAjax(this);”>TEST</a></h3>

<h3><a href=”javascript:void(0);” onclick=”testAjaxParms(this);”>TESTPARMS</a></h3>

<h3><a href=”javascript:void(0);” onclick=”testAjaxXSS(this);”>TESTXSS</a></h3>

<h3><a href=”javascript:void(0);” onclick=”testAjaxHook(this);”>TESTHOOK</a></h3>

<h3><a href=”javascript:void(0);” onclick=”testAjaxHookXSS(this);”>TESTHOOKXSS</a></h3>

<p><a href=”index.php”>RELOAD</a></p>

</body>

</html>

Response program (ajax.php)

<?php

header(“Cache-Control: no-store”);

header(“Charset: UTF-8”);

header(“Content-Type: text/xml”);

echo(“<?xml version=\”1.0\” encoding=\”UTF-8\”?>\n”);

?>

<test><?php echo( gmdate(“D, d M Y H:i:s”) ) ?> </test>

What are the MSIE Bugs you might ask… these are all in the ActiveX implementation of XmlHttpRequest, they are:

  1. Caching – for some reason, unless you define a ‘unique’ URL for each request, MSIE6 will respond from the cache, even if the server is unreachable!
  2. XSS – I discussed this above, MSIE6 will allow you to connect to ANY HOST with AJAX!
  3. Memory leak – if you don’t nullify the XHR object (for MSIE6), the application will leak memory.

Cheers!

MSIE’s flawed SSL implementation

This has been quite frustrating. It seems that Microsoft has again ventured from complying with the industry web standards in this space too!

The comments from the Apache HTTP 2.x ‘http-ssl.conf’ files say it all:

#   SSL Protocol Adjustments:
#   The safe and default but still SSL/TLS standard compliant shutdown
#   approach is that mod_ssl sends the close notify alert but doesn't wait for
#   the close notify alert from client. When you need a different shutdown
#   approach you can use one of the following variables:
#   o ssl-unclean-shutdown:
#     This forces an unclean shutdown when the connection is closed, i.e. no
#     SSL close notify alert is send or allowed to received.  This violates
#     the SSL/TLS standard but is needed for some brain-dead browsers. Use
#     this when you receive I/O errors because of the standard approach where
#     mod_ssl sends the close notify alert.
#   o ssl-accurate-shutdown:
#     This forces an accurate shutdown when the connection is closed, i.e. a
#     SSL close notify alert is send and mod_ssl waits for the close notify
#     alert of the client. This is 100% SSL/TLS standard compliant, but in
#     practice often causes hanging connections with brain-dead browsers. Use
#     this only for browsers where you know that their SSL implementation
#     works correctly.
#   Notice: Most problems of broken clients are also related to the HTTP
#   keep-alive facility, so you usually additionally want to disable
#   keep-alive for those clients, too. Use variable "nokeepalive" for this.
#   Similarly, one has to force some clients to use HTTP/1.0 to workaround
#   their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
#   "force-response-1.0" for this.

SetEnvIf User-Agent ".*MSIE.*" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0

A little further research indicates that MSIE6 has (probably) partially fixed this (the HTTP/1.0 & KeepAlive issues), so the updated config should use a Regular Expression to look like…

SetEnvIf User-Agent ".*MSIE [1-5].*"
 nokeepalive ssl-unclean-shutdown
 downgrade-1.0 force-response-1.0
SetEnvIf User-Agent ".*MSIE [6-9].*"
 ssl-unclean-shutdown

Related Information:

Cheers!