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:

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.