Skip navigation.

How Much More Basic Do We Need To Get?All recent postsSurvival of the Unfittest

Dissecting Adaptive Rendering

In ASP.NET some server controls render different markup based on the visiting browser. The notion of Adaptive Rendering is what is behind it. We've talked about it before, but I wanted to dig deeper into the internals of adaptive rendering and illustrate how it works.

At one point in the lifecycle of a page, the Request object inquires about capabilities of the visiting browser. It looks into machine.config and parses its browserCaps section.

Next, it obtains the browser's user agent string and tries to match it to one of the regular expressions listed in browserCaps. Once a match is found, it constructs an instance of the HttpBrowserCapabilities object.

This instance of HttpBrowserCapabilities has a lot of useful information and is used for a number of purposes. The property that we're concerned about is called TagWriter.

The way machine.config is pre-configured in ASP.NET 1.x there are two tag writers (more about them shortly) it uses to render HTML. For Internet Explorer/Win ver 4.x, 5.x and 6.x it uses System.Web.UI.HtmlTextWriter. For all other browsers—System.Web.UI.Html32TextWriter.

Why Two Tag Writers?

ASP.NET saw light in 2000, but it was in the works for several years prior to that. My guess is nobody wanted to revisit the approach of adaptive rendering once it was in place, even though it was outdated. Html32TextWriter is used to play safe. Its "preferred rendering type" is HTML 3.2 (via MSDN):

This class is an alternative to HtmlTextWriter. It converts HTML 4.0 style attributes into the equivalent tags and attributes compatible with HTML 3.2. It standardizes the propagation of attributes, like colors and fonts, using HTML tables, which can vary in behavior in earlier browsers. The ASP.NET Web Forms automatically uses this class for HTML 3.2 and earlier browsers by checking the TagWriter property of the HttpBrowserCapabilities class.

It's a subject for a heated debate which browsers are superior and which one(s) are "downlevel", but the fact is Internet Explorer/Win is badly outdated and yet is given the upper hand, while more advanced browsers are still treated by the engine as "downlevel" and their rich capabilities aren't taken advantage of. I know, I know...

Html32TextWriter extends HtmlTextWriter and overrides some of its virtual methods. It has a peculiar method, GetTagName:

protected override string GetTagName(HtmlTextWriterTag tagKey)
{
 if (tagKey == HtmlTextWriterTag.Div)
  return "table";

 return base.GetTagName(tagKey);
}

As you see, whenever the writer sees a div it swaps it for a table! Another funky method is OnTagRender:

protected override bool OnTagRender (string name, 
    HtmlTextWriterTag key)
{
 ...
 if (key == HtmlTextWriterTag.Div)
   base.TagKey = HtmlTextWriterTag.Table;

 return base.OnTagRender(name, key);
}

Notice the transformation of div into table. Finally, check out RenderBeginTag on your own, but it behaves along the same lines.

Applying a Tag Writer

Now that we know which tag writers the pipeline may use, let's see where they are applied.

When we develop a web page, we ultimately derive from the System.Web.UI.Page class. It's baked into the default Web Form template Visual Studio.NET uses, so we don't even think about it most of the time.

Now, at a certain point the page tells all of its server controls to render themselves. It passes each control an instance of the tag writer it created previously (see above):

protected virtual void Render (HtmlTextWriter writer);

How does the page know which type of tag writer to create? The logic is in its CreateHtmlTextWriterInternal method:

internal static HtmlTextWriter CreateHtmlTextWriterInternal (
  TextWriter tw, HttpRequest request)
{
 if ((request != null) && (request.Browser.TagWriter != null))
 {
  return Page.CreateHtmlTextWriterFromType (tw,
                          request.Browser.TagWriter);
 }
 return new Html32TextWriter(tw);
}

You see that Html32TextWriter also serves as a default fall-back writer in case the pipeline failed to determine browser capabilities. Finally, CreateHtmlTextWriterFromType uses the type of writer from the TagWriter property of HttpBrowserCapabilities carried through the request (see above).

Everything nicely comes full circle.

Every control receives an appropriate tag writer and renders its content through it. The writer chooses whether to alter what's going though it to bend it to whichever version of HTML it's supposed to output. The UML diagram below illustrates this point:

UML diagram of adaptive rendering

Which Controls Are Subject To Adaptive Rendering?

The one that comes to mind is the Panel control. This is how it's constructor is defined:

public Panel() : base(HtmlTextWriterTag.Div) {}

What happens to div passed through Html32TextWriter you already know (if not, scroll back up to the top and read this post again).

Remember, adaptive rendering isn't only about annihilating divs. In general, when Html32TextWriter is applied it ensures the markup is HTML 3.2 with its font tags and the like. Therefore, other controls, besides Panel, may appear differently when their styles are set.

Deviations

Yes, you may modify the user agent RegEx in machine.config to have all browsers use HtmlTextWriter and do away with adaptive rendering. There's a problem with this, though: ASP.NET will inject JavaScript which powers validation controls. Microsoft developers decided to stick to their proprietary DOM model which is why client-side validation works only in IE.

What's going to happen is validators won't work on the client, so we're back to square one: don't change browserCaps because it won't do you any good anyway.

Do not modify machine.config unless you really know what you're doing.

A feasible solution is to find (or buy) replacement scripts that work across all browsers. You might also want to buy replacement server controls which work in tandem with validation scripts properly. Sounds like a lot of hassle, but it if you feel strongly about it, go for it!

Let Opera Be Opera

Depending on how your Opera is configured, it may identify itself as Internet Explorer. My advice is to leave it alone and let Opera be itself by sending its very own user agent string. If it fakes IE, ASP.NET will output validation scripts which won't work anyway because of the Microsoft's proprietary DOM, so the net gain is zero.

Comments

Comment permalink 1 Mike Gale |
A nice piece of work. Thanks.

I looked into machine.config while I was reading it. A wealth of information. (Reminded me strongly of the BrowsCaps system in ASP and the way it became orphaned and abandoned!)

It's unfortunate that this system isn't easy to improve. It seems not to be worthy of it's privileged position, touching all content that passes through it.
Comment permalink 2 Adam |
Excellent article - it answered the question, as to why my pages were breaking in Firefox perfectly.

Firefox, with the UserAgent Switcher extension seems to work fine when set to IE6. Obviously, some extra testing of validation and so on is required, but most pages should work OK if the browserCaps section of machine.config is updated to use HTML4 rendering for this browser.

However, where are the scripts located on the server, that are sent to the browser for validation, postback and so on?

It shouldn't be too hard to update any problems with these to work with other browsers (so long as the originals are backed up first!! ;-)

Thanks again
Comment permalink 3 Milan Negovan |
Validation scripts are located in C:\Inetpub\wwwroot\aspnet_client\system_web, or wherever you installed IIS. Look for WebUIValidation.js.

Postback scripts are generated on the fly.

Personally, I think it's better to leave validation scripts alone for now and make sure you always check Page.IsValid on postbacks.
Comment permalink 4 Venkat |
Hi,

I am developing web application using C# and asp.net.I am getting problem with cross browser.When run my application in IE, it is working fine.when i tested in morzilla , the controls are displacing.I do not why where i am doing wrong.

In my CSS file i have take Position as absolute.But also I am getting same problem.
anyone can guide me.

Thanks,
Venkat

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):