See Joel Program

September 4, 2008

Adding Google Chrome to ASP.NET AJAX

Filed under: AJAX, ASP.NET AJAX, JavaScript — Joel Rumerman @ 8:21 pm

12/15/2008 – Two things to update:

First, sources at Microsoft confirmed this bug for me and said that a fix was already completed for v-next. I’m not sure when the next release will take place; if it will be with VS 2010 or sometime earlier, but they have it fixed.

Second, Jason Kealey has a fix that is less intrusive than the one I suggested here. You can find it here

Since the rest of the tech-world is talking about and trying Google Chrome, I thought that I’d give it a whirl and see how it interacted with ASP.NET AJAX. I’ve worked on a couple of ASP.NET AJAX applications for work (including a public website: http://showcase.costar.com) and have a bunch of small test application that I’ve written for other purposes and was curious to see how those held up when accessed with Chrome. For the most part, with the smaller applications the browser worked as expected. I had almost no layout issues (as we program for IE6, IE7, FF2, and FF3) and almost no JS problems. But, when I ran ShowCase, which uses UpdatePanels to load large swathes of a page and in doing so loads external scripts dynamically, the application broke when accessing the mapping portion of the application. Since I was responsible for this part of the application, I needed to investigate further.

Through Fiddler, which Chrome interacts with nicely by default, I discovered that my scripts weren’t loading. Furthermore, through Chrome’s JavaScript console I learned that a JS error was being thrown. The error thrown was the one where it says the external script at xyz.js doesn’t contain a call to Sys.Application.notifyScriptLoaded(). Well, all my scripts contain this call and so this was a red herring, but set me in the right direction.

Thinking about it, I remembered that script loading during a partial postback is a browser specific task and I wondered what code path Google Chrome was following since it isn’t a known browser to ASP.NET AJAX. The first thing I needed to discover was what browser ASP.NET AJAX thought it was interacting with. To my surprise, ASP.NET AJAX thought Chrome was Safari.

I discovered this using Chrome’s JavaScript console and executing the “Sys.Browser.name” command, which outputted the value “Safari.” (BTW, the IntelliSense in the console window is amazing.) At first, I thought that Safari might be the default browser when the ASP.NET AJAX code can’t figure out what browser it really is. While this would be a bit strange, maybe it made sense when MSFT developed ASP.NET AJAX. So I went hunting inside of MicrosoftAjax.js to figure out why ASP.NET AJAX thought that Chrome was Safari and came to this code.

if (navigator.userAgent.indexOf(' MSIE ') > -1) {
    Sys.Browser.agent = Sys.Browser.InternetExplorer;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(/MSIE (\d+\.\d+)/)[1]);
    if (Sys.Browser.version >= 8) {
        if (document.documentMode >= 7) {
            Sys.Browser.documentMode = document.documentMode;
        }
    }
    Sys.Browser.hasDebuggerStatement = true;
}
else if (navigator.userAgent.indexOf(' Firefox/') > -1) {
    Sys.Browser.agent = Sys.Browser.Firefox;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(/ Firefox\/(\d+\.\d+)/)[1]);
    Sys.Browser.name = 'Firefox';
    Sys.Browser.hasDebuggerStatement = true;
}
else if (navigator.userAgent.indexOf(' AppleWebKit/') > -1) {
    Sys.Browser.agent = Sys.Browser.Safari;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(/ AppleWebKit\/(\d+(\.\d+)?)/)[1]);
    Sys.Browser.name = 'Safari';
}
else if (navigator.userAgent.indexOf('Opera/') > -1) {
    Sys.Browser.agent = Sys.Browser.Opera;
}

Obviously, my initial assertion that Safari might be the default was wrong. Seeing that the code uses the userAgent string to determine the browser, I grabbed the userAgent string of Chrome request in Fiddler:

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13

As the code above shows, it parses the userAgent string looking for the word ” AppleWebKit/” and if found (once it didn’t find ” MSIE ” or ” Firefox/”) the if-then logic determined that the browser is Safari. Now that I understood why ASP.NET AJAX thought that Chrome was Safari, I also had an idea about why my map was failing to load.

Searching through MicrosoftAjax.js, I found that there are three code paths that being a “Safari” browser alters.

  1. JSON Serialization
  2. External script loading and script text processing during a partial postback
  3. Getting a location of an element

Obviously, since the culprit of my problem was that my external scripts weren’t being loaded during a partial postback, this was the problem I needed to fix. Without knowing exactly what to do, I decided to see if I loaded external script using the default code path instead of the Safari code path, would the scripts load properly. To achieve this I needed to get Chrome to not be recognized as Safari so it would go down the default code path.

To add the Chrome browser to ASP.NET AJAX, there are two basic steps.

1. Add a new browser type

Sys.Browser = {};
Sys.Browser.InternetExplorer = {};
Sys.Browser.Firefox = {};
Sys.Browser.Safari = {};
Sys.Browser.Opera = {};
Sys.Browser.Chrome = {};

2. Update the if-then logic to search for Chrome

else if (navigator.userAgent.indexOf(' Firefox/') > -1) {
    Sys.Browser.agent = Sys.Browser.Firefox;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(/ Firefox\/(\d+\.\d+)/)[1]);
    Sys.Browser.name = 'Firefox';
    Sys.Browser.hasDebuggerStatement = true;
}
else if (navigator.userAgent.indexOf(' Chrome/') > -1) {
    Sys.Browser.agent = Sys.Browser.Chrome;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(/ Chrome\/(\d+\.\d+)/)[1]);
    Sys.Browser.name = 'Chrome';
    Sys.Browser.hasDebuggerStatement = true;
}
else if (navigator.userAgent.indexOf(' AppleWebKit/') > -1) {
    Sys.Browser.agent = Sys.Browser.Safari;
    Sys.Browser.version = parseFloat(navigator.userAgent.match(/ AppleWebKit\/(\d+(\.\d+)?)/)[1]);
    Sys.Browser.name = 'Safari';

Notice how the Chrome check has to go before the Safari check.

(This is basically the same pattern that’s talked about here for Safari 3 before .NET 3.5 SP1 came out. Has anybody else noticed the decreasing font size in the ASP.NET forum’s when viewing with Chrome?? Hilarious)

Now, when we run an ASP.NET AJAX site using our custom MicrosoftAjax.js file (to see how to replace Framework scripts with custom ones, read this article), Sys.Browser.name returns “Chrome.”

So back to ShowCase. Using my customized MicrosoftAjax.js file, ShowCase mapping worked like a champ.

I’ll conclude with a caveat. I didn’t check the Get Location or the Serialization code as it wasn’t needed for me to get the application up and running, but it looks okay at the moment since my element are positioned correctly and everything is serializing / deserializing. Then again, I don’t have any unit tests.

7 Comments »

  1. […] Source […]

    Pingback by Adding Google Chrome to ASP.NET AJAX « Rich Internet Applications — September 5, 2008 @ 12:04 pm

  2. Somebody ought to create an override for emoticons…

    Sys.Browser.version >= [smiley with shades here]

    Comment by Nitin Reddy Katkam — September 28, 2008 @ 10:23 pm

  3. For the uninitiated…

    Sys.Browser.version >= 8

    appears as

    Sys.Browser.version >= [smiley with shades here]

    Comment by Nitin Reddy Katkam — September 28, 2008 @ 10:25 pm

  4. Yes, I left out the bracket after the ‘8’intentionally in the previous post, just in case the emoticons took over ‘comment planet’ too.

    Comment by Nitin Reddy Katkam — September 28, 2008 @ 10:26 pm

  5. I was stunned to see chrome’s requests showing up in fiddler. I assume this means that chrome uses the Windows Http framework AKA the IE frame work ?? Stunning !

    So the Apparent IE killer actually uses a big chuck of IE…

    No problem with this – just surprised the are not getting laughted out of town for this. I like chrome and everythign – but lets face it, it’s hype !

    Perhaps I’ve got this wrong.

    Comment by kdobey — October 1, 2008 @ 8:47 am

  6. You know, I’m not sure what it truly means that Chrome requests show up through Fiddler automatically.

    I mean, Fiddler is just an HTTP proxy that intercepts requests sent on a given port and forwards them on.

    I know you can customize Firefox so that its requests are routed through Fiddler, but that takes customization. I just don’t know what it means for Chrome to automatically have its requests display in Fiddler. Does it mean that Fiddler utilizes some of Microsoft’s inet library, which would be surprising, or does it simply mean that it recognizes an HTTP proxy and knows what to do with it.

    I really don’t know.

    Comment by Joel Rumerman — October 1, 2008 @ 8:54 am

  7. I know that this is an old post, but I found a simple solution here:

    http://stackoverflow.com/questions/1247032/asp-net-dynamic-validators-dont-work-in-chrome-or-safari

    Comment by Rafael Augusto — April 18, 2011 @ 10:32 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: