I CAN HAS CODE?!?

Archive for June, 2008

WebForms Knows Better Than You!

Thursday, June 19th, 2008

Sometimes I get annoyed with ASP.NET WebForms … No, make that greatly annoyed and disappointed with web forms.  As I mentioned in my last post, I was recently troubleshooting a 2nd incarnation of the mysterious multiple request syndrome (MMRS for short).  What made this time worse was what ending up being the cause.  In this case the request was being caused by a “url(none)” in a style attribute.  After tracking down the offending component I found something similar to the following example in the code.

Panel inner = new Panel();
inner.ID = "inner";
inner.Style.Add("background-image", "none");

Notice the last line.  Looks correct doesn’t it?  WRONG!  It’s not correct because WebForms Know Better Than You! ™.  When the above is rendered out to the page, it looks like this:

<div id="inner" style="background-image: url(none);"> </div>

Notice anythign wrong?  Yep, right there in the middle … the wonderful “url(none);” and a 2nd request in your browser for the “/none” path.  Well, poo… How did that get in there?  More importantly why did it get in there?  Especially considering the CSS spec specifically allows “none” as a value for the “background-image” property.  In fact the w3 page for the CSS1 (and CSS2) spec says “Value: <url> | none”.

Being the inquisitive sort, there was only one thing that I could do;  Fire up Reflector.NET and go to town.  First order of business was to find the Style property for the Panel control, which ends up being the Style property of the Panel’s base class, WebControl.  It is of type CssStyleCollection … My answer must lie somewhere in there.  The Add method in the CssStyleCollection class isn’t that special and just adds the items to some hash tables.  The Render methods might be interesting however.  After using the Analyzer on those and seeing how they are used we can determine two things.  First, the Render method that takes an HtmlTextWriter merely calls AddStyleAttribute on the writer and adds the CSS properties while the other one takes a CssTextWriter … Wait, what?  That’s a bit different.  It seems to call a WriteAttribute method of the CssTextWriter class.  After making our way to the static WriteAttribute method, we see the following:

if (key != ~HtmlTextWriterStyle.BackgroundColor)
{
    isUrl = attrNameLookupArray[(int) key].isUrl;
}
if (!isUrl)
{
    writer.Write(value);
}
else
{
    WriteUrlAttribute(writer, value);
}

Hmm, what is this “isUrl” and WriteUrlAttribute? Let’s go find out.  A quick Analyze on the “attrNameLookupArray” member shows that it is set in the static constructor so off we go.  In there we see a bunch of RegisterAttribute calls and we see one for the property we set at the beginning of this ordeal, “background-image”.  Digging into the RegisterAttribute method, we see that the last parameter to the function, which in the case of “background-image” is “true”, is the “isUrl” value we are looking for.  So the CssTextWriter considers the “background-image” attribute a URL based attribute … Makes sense, as it is one, so the issue must lie somewhere within WriteUrlAttribute.  Looking at the code for that function:

if (StringUtil.StringStartsWith(url, "url("))
{
    int startIndex = 4;
    int length = url.Length - 4;
    if (StringUtil.StringEndsWith(url, ')'))
    {
        length--;
    }
    str = url.Substring(startIndex, length).Trim();
}
// str is rendered out as "url(" + str + ")" after.

Well, that is unfortunately where are issue lies and there isn’t a damn thing we can do about it.  No special handling of “none”; Just a regurgitation of whatever we set the property to.  That is just too awesome.

The Mystery of the Multiple Requests

Thursday, June 19th, 2008

I’ve ran into this issue for the second time today and it was as much of a pain to troubleshoot it as it was the first time around so I figured I’d do a little write-up about it so that others may save themselves a good bit of frustration. 

The first time I encountered the issue, it seemed to happen at random.  I was stepping through some code for the web application I was working on at the time in the debugger and had a breakpoint set that should have only ever been reached once per request and yet this time it was reached twice.  As I was going through some testing, the problem would go away for a time, and then come back, depending on the state of the page. 

Enter the mysterious request… So instead of trying to troubleshoot any further in the debugger I decided it would be good to look at the requests as they happen using FireBug (or Fiddler).  Sure enough, there were two requests anytime I had a certain component visible on the page.  So that narrowed it down further at least in that I knew what component was causing it.  What gave me enough to google on was the fact that the request headers for the 2nd request were different in one very important way, the "Accept" header.  Instead of the usual "text/html" plus friends it was "image/png,image/*" … which indicates it was a request for an image resource.

Armed with that information, I prayed before the google gods and found a couple of posts (seriously, there were only two that I could find) about the issue and one mentioned an empty "src" attribute on an image tag.  That wasn’t the case that time, but it got me thinking … If image tags could do it, what about CSS properties?  Sure enough, that was the problem.  There was one CSS property defined in a style attribute that was "background-image: url();".  Taking care of the offending property made the issue go away and everyone was happy.

Now, there are a number of things that can cause this situation and they are outlined below.  There are also multiple ways that the issue will show itself in a request log. 

An empty "src" attribute on an image tag.

<img src="" />

An emtpy url definition on a CSS property or a url definition with "none"* inside of it.

.test {
background: #ffffff url();
background-image: url('');
background-image: url(none);
}

I’ll be talking more about the last one in a follow up post.

As for how they can show up in a request log:

  • A second request for the same path and query. 
  • A second request for the root path (i.e. the normal request is for "/default.aspx" and the second request is for "/").
  • A second request for the "none" path (i.e. "/none").

In Firefox the second request will usually always have the "Accept: image/png,image/*" header.  Unfortunately that is not always the case with IE.  IE likes to happily send "Accept: */*" along.

Now how do you go about finding these in your code and markup?  Unfortunately that is not always that easy and you may have to resort to using multiple tools.  A good start is the HTML tab in FireBug.  Another good option is "View -> Source -> DOM" option in the IE Developer Toolbar.  Another thing to do is to search your solution for the offending items.  Searching with regular expressions can be a big help there.

I hope this post proves helpful to someone else that encounters the problem.  It was almost as frustrating to find the culprit the second time as it was the first time and I knew what I was looking for … though part of that problem was WebForms working against me, but more on that later.

First Post!

Tuesday, June 17th, 2008

It’s finally here! After much procrastination I have finally gotten around to setting up my blog. My plan is to try and post at least once a week and to keep the topics primarily development and web design related. However, with a title like “I Can Has Code?!?”, I’m sure there will be plenty of room for less “structured” posts.

As for how the name came about… Well, I’m a big fan of lolcats, and while designing the site in photoshop, I typed it in as a placeholder and it just kinda stuck :)