Our database software runs on Microsoft’s .NET platform. The newest version of that software switched to .NET 4.0. While we have not yet updated to that version another church using the same software did last week. They also use the same children’s check-in software that we do. Everything went pretty smooth for them except for one problem that only cropped up after the upgrade on the production server – Check-in no longer worked. Minor issue when you have 800 kids going to be checked in over the weekend services.
The issues were basically weird things were happening. Via some debugging we found that it was being caused by the AJAX script not being included by IIS. Some googleing around on that revealed that was caused by IIS believing the browser did not support Javascript. Obviously the browser does support it so it was just a matter of why IIS was believing that. All the solutions online required us to modify the .aspx page, but everything we work with is .ascx user controls, we don’t have control over the .aspx page code, so we needed a better solution not just a bandaid.
Long story short, we worked through various tests and google results and came up with 2 causes for the problem.
Issue #1 – Microsoft thinks all UserAgent strings are short.
This may have been a change in .NET 4 or it may have always been this way, not sure. But in .NET 4 at-least IIS assumes all UserAgent strings will be shorter than 64 characters. We were very confused because the “windowed” Safari (i.e. tapping Safari and browsing) would usually work, but randomly would stop working. The full-screen Safari (going to a web-page and then adding it to the home-screen) would usually not work, but randomly would work. This was caused by the fact that IIS (also possibly new in .NET 4.0) caches the browser capabilities based on the UserAgent string given by the browser. Here is the catch. That is by default limited to 64 characters. So if two different browsers have 2 different UserAgent strings but the differences are AFTER the first 64 characters, they will be treated the same. Take a look at this website for a list of browser strings through the years, going all the way back to the days of AOL. UserAgent strings have always been longer than 64 characters, Microsoft really dropped the ball on this one.
The fix for this was to add <browserCaps userAgentCacheKeyLength=”256″ /> inside the <system.web> section of the web.config file for the website. This increases the cached key from 64 characters up to 256, which is long enough to store all browser strings. Really 128 would probably have been enough, but why risk it? After this fix was in place we got consistent results from windowed Safari and full-screen Safari. The former always worked, the latter never worked. So the caching of browser capabilities is what was causing things to be intermittent. We were then able to move on to fixing the real problem: full-screen Safari not being treated as a Javascript capable browser.
Issue #2 – Apple thinks full-screen Safari should not have a version number.
Say what? Yes. The UserAgent string for Safari when in full-screen mode (maybe embedded browsers too, not sure) does not include a Version number or Safari build number. Here are samples of the two strings:
Regular Safari: Mozilla/5.0 (iPad; CPU OS 6_1_3 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25
Full-screen Safari: Mozilla/5.0 (iPad; CPU OS 6_1_3 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10B329
You may also note that character 64, where the UserAgent was being truncated to for caching purposes, is inside the “536.26” version number of the AppleWebKit, before the point at which it would realize they are different. Prior to .NET 4.0, Microsoft’s browser matching had a much simpler browser detection which detected the Full-screen Safari as a generic “smart” browser. The new detection code is much more specific and detects Full-screen Safari as, essentially, the first web-browser ever created. Meaning completely feature-less. Basically it doesn’t match anything so it gets thrown into the “default” browser match.
What we had to do was build a pattern match that would detect Full-screen Safari and treat it as a real browser. Because we don’t have specific version numbers to test against we treat then all as Safari version 3. In .NET 4.0 Safari 3, 5 and 6 are all treated as Safari 3 browsers. Safari 4 is treated as Safari 4 (but only bumps one capability version number from 1.6 to 1.7), so we played it safe and went with Safari 3 since that is what current versions of Safari are treated as anyway. Here is what we had to do. In the folder that contains your web.config you need to add a App_Browsers folder and then drop the browser file in there for it to be used by IIS. The file can be named anything as long as it ends with .browser though we named ours safari_mobile_fullscreen.browser.
Once we dropped the below code into that folder, the second problem was resolved which solved everything. They reported perfect success on the weekend children’s check-in system.
<browsers>
<!-- Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-ch) AppleWebKit/125.5.5 (KHTML, like Gecko) Safari/125.12 -->
<!-- Prior to Safari 3, the Safari user-agent string did not include a version number -->
<browser id="SafariMobileFS" parentID="Mozilla">
<identification>
<userAgent match="AppleWebKit" />
<userAgent nonMatch="Chrome" />
<userAgent nonMatch="Safari" />
</identification>
<capture>
<userAgent match="AppleWebKit/(?'layoutVersion'\d+)" />
</capture>
<capabilities>
<capability name="layoutEngine" value="WebKit" />
<capability name="layoutEngineVersion" value="${layoutVersion}" />
<capability name="browser" value="Safari" />
<capability name="type" value="Safari" />
<capability name="version" value="3" />
<capability name="majorversion" value="3" />
<capability name="minorversion" value="0" />
<capability name="type" value="SafariFS" />
<capability name="ecmascriptversion" value="3.0" />
<capability name="javascript" value="true" />
<capability name="javascriptversion" value="1.6" />
<capability name="w3cdomversion" value="1.0" />
<capability name="tagwriter" value="System.Web.UI.HtmlTextWriter" />
<capability name="cookies" value="true" />
<capability name="frames" value="true" />
<capability name="javaapplets" value="true" />
<capability name="supportsAccesskeyAttribute" value="true" />
<capability name="supportsCallback" value="true" />
<capability name="supportsDivNoWrap" value="false" />
<capability name="supportsFileUpload" value="true" />
<capability name="supportsMaintainScrollPositionOnPostback" value="true" />
<capability name="supportsMultilineTextBoxDisplay" value="true" />
<capability name="supportsXmlHttp" value="true" />
<capability name="tables" value="true" />
</capabilities>
</browser>
</browsers>