Server-Side Browser Detection and Content Delivery : Mobile Detection (part 4) - Device Libraries

1/29/2011 6:49:03 PM

4. Device Libraries

Just looking at the HTTP headers and the UAProf will not give us enough useful information about the mobile devices that are accessing our websites. This is where device libraries come to our help. Device libraries are offline databases (or online web services) that take a user-agent string (or all of the request headers) and return to us dozens of properties about the detected device, from screen size, to Java ME compatibility, to Ajax support and video codec compatibility.

4.1. WURFL

Wireless Universal Resource File (known as WURFL) is a community-based, open source device capabilities repository created and maintained by the developer Luca Passani (, a fellow member of the Forum Nokia Champion program and author of the preceding section on transcoders.

The library is available in the form of an XML file. While updates to WURFL data happen every day on the WURFL DB, a publicly available “snapshot” of the DB is produced and published about once per month on the WURFL website.

WURFL can be downloaded for free from, and any suggestions, questions, or bugs can be discussed in the mailing list,


If you are having doubts about how to pronounce WURFL, you can find a WURFL pronunciation link on the library’s home page, where you can listen to the word in English and Italian.

The sources of information include official technical information published by manufacturers, UAProf files, and data collected by the community after testing on real devices. Today, the WURFL database contains thousands of devices (11,000 profiles, 7,000 of which have a unique brand and model), with information on subversions, operating systems, firmware and hardware variations, and hundreds of attributes that we can query for each one.

4.1.1. Architecture

WURFL groups the devices into a hierarchy of devices and attributes. Some devices are equivalent to other devices from the same series, possibly with some new features, so there is a fallback mechanism in WURFL allowing a device to extend the features of another one. The same applies for different models in the same brand or even different brands with the same operating system.

Also, there is a feature called “actual device root” that manages multiple subversions (different firmware) of the same device, so the information is not duplicated in two records; the subversion will be based on the main record with any added or different abilities noted.

The WURFL database has a root fallback device called “generic device” that is matched when the device, the brand, and the series can’t be determined.

4.1.2. Patch file

What should you do if you need to make changes to the WURFL XML, whether to identify new devices or bugs that you’ve found or to add new private or public capabilities to be queried? Changing the original WURFL XML would be impractical, because you would have problems in the future when you wanted to download updates from the site.

That is why a patch file is included in the WURFL architecture. A patch is like a mini-WURFL (similar syntax, but typically a lot smaller in size). The WURFL API will merge the patch information with the information in the WURFL database when the service starts.


If you find a bug, new devices, or capabilities that aren’t private to your development, report them to the WURFL team or apply to become a WURFL contributor.

You can register to become a contributor by following the instructions at You will be required to study religiously the WURFL conventions, available at

Remember, this is a community project, and we are all part of the community.

WURFL is intended for use on mobile websites; that is why this library does not detect desktop browsers, or if they are detected misidentifies them as some fallback mobile. If users may access your mobile website from their desktops and you want to be able to detect that using WURFL, you can download a web patch that will detect desktop web browsers like Firefox and Internet Explorer. You’ll need to merge this patch with the main XML.


If you find a generic_device, this is a device that is not included in the WURFL database. It’s good practice to log and report this information and the user agent received so it can be investigated and recognized in the future.

4.1.3. Capabilities

Every ability, property, or attribute is called a capability in the WURFL world. Capabilities are organized into groups. Each capability for each device has an optional string value (taken from the device itself, or from the fallback mechanism). That value can be converted to a Boolean, a number, a string, or an empty string.

The most useful groups at the time of this writing are shown in Table 3.

Table 3. Most useful WURFL capability groups
Group nameCapabilities related to
product_infoDevice information, such as the brand, model, operating system, and browser
wml_uiWML rendering, including soft key support, WTAI support, and table support
chtml_uicHTML rendering
xhtml_uiXHTML rendering, including tel URI scheme support, accesskey support, iframe support, and file upload support
ajaxAjax and DOM support, including support for getElementById, innerHTML, and CSS manipulation
markupMarkup compatibility
cacheCache support
displayThe screen and display (physical dimensions, resolution, line rows, etc.)
image_formatImage formats, including support for Animated GIF and SVG
wtaWTAI, including voice call support
securityEncryption, including HTTPS support
bearerNetworks, including WiFi and VPN support
storageLimits (e.g., max URL length)
object_downloadFormats and object downloading support for each typical format
streamingAudio and video streaming per format and codec
wap_pushWAP Push attribute support
j2meJava ME configuration and profile versions and API compatibility
mmsMMS support
smsSMS support
sound_formatSupport for audio codecs and formats
flash_liteFlash support on the browser, for standalone applications, and for wallpaper or screensavers
cssCSS properties
transcodingWhether the client is detected as a transcoder
rssRSS support
pdfPDF viewing support
playbackFormats that can be played by the device

As you can see, the information that the XML provides is really complete. If you want to browse all the capabilities, you can browse the XML with any reader or use the tools that come with the Java API. You can check the first device definition, as the generic device and fallback for all devices. If any property is not defined in the generic device, there will be no fallback value to use if the device does not define it.

Table 4 shows the most important capabilities we can query. Remember that there are dozens of other properties that you can query; take a look at the library so you’ll have an idea of all the possibilities.

Table 4. Most useful WURFL capabilities
Capability nameTypeIndicates...
brand_nameStringThe device’s brand name (e.g., Apple, Nokia, or HTC)
model_nameStringThe device’s model name (e.g., iPhone, N97, Nexus One)
marketing_nameStringThe device’s marketing name, including the brand, model, and possibly another part of the name (e.g., Pearl, Touch)
is_wireless_deviceBooleanWhether the device is a mobile device (true) or a desktop/notebook
pointing_methodStringWhich pointing method is accepted (joystick, stylus, touchscreen, clickwheel, or the empty string)
has_qwerty_keyboardBooleanWhether the device has a QWERTY keyboard (virtual or physical)
nokia_seriesIntegerThe series (40, 60), for Nokia devices
nokia_editionIntegerThe edition of the series, for Nokia devices
nokia_feature_packIntegerThe feature pack of the series, for Nokia devices
device_osStringThe name of the operating system
device_os_versionStringThe version of the OS
mobile_browserStringThe name of the browser
mobile_browser_versionStringThe version of the browser
resolution_widthIntegerThe screen width in pixels
resolution_heightIntegerThe screen height in pixels
max_image_widthIntegerThe display’s usable width in pixels
max_image_heightIntegerThe display’s usable height in pixels
xhtml_support_levelIntegerThe level of XHTML compatibility, from −1 to 4:
  • −1: No support

  • 0: Basic support (poor or no CSS support, basic form support, basic or no table support)

  • 1 and 2: Advanced basic support (basic CSS and table support)

  • 3: Medium support, including excellent CSS support)

  • 4: Advanced support, including Ajax support

preferred_markupStringThe markup best supported by the device (even if it supports a newer one)
xhtml_format_as_css_propertyBooleanWhether -wap-input-format is available
xhtml_make_phone_call_stringStringThe prefix preferred for making phone calls in a URL
xhtml_send_sms_stringStringWhether and how the device supports triggering the SMS client from a link (can be sms:, smsto:, or the empty string, meaning not supported)
xhtml_file_uploadStringWhether the device allows file uploading (returns not_supported, supported, or supported_user_intervention)
xhtml_supports_iframeStringWhether the device supports iframes (returns none, partial, or full)
ajax_supports_javascriptBooleanWhether the device supports JavaScript with basic operations (dialogs, form values, timers, and document.location)
ajax_supports_getelementbyidBooleanWhether document.getElementById works on the device
ajax_xhr_typeStringWhich syntax to use when creating an XMLHTTPRequest object (none, standard for the native XHR object, msxml2 for the normal Microsoft ActiveX object, and legacy_microsoft for the older one)
ajax_support_inner_htmlBooleanWhether we can change the innerHTML property dynamically
ajax_manipulate_domBooleanWhether typical DOM methods are available
ajax_support_event_listenerBooleanWhether the browser allows event registration through event listeners
html_wi_oma_xhtmlmp_1_0BooleanWhether the browser supports XHTML MP 1.0
html_web_3_0BooleanWhether the browser supports HTML 3
html_web_4_0BooleanWhether the browser supports HTML 4
gif_animatedBooleanWhether Animated GIF is supported
svgt_1_1BooleanWhether SVG 1.1 is supported
svgt_1_1_plusBooleanWhether SVG 1.1+ is supported
flash_lite_versionStringWhich version of Flash is supported
fl_browserBooleanWhether the browser supports Flash content
is_transcoderBooleanWhether a transcoder was detected as a proxy from the real device
transcoder_ua_headerStringWhich header we can find the original device’s user-agent string in, if a transcoder was detected
multipart_supportBooleanWhether the browser supports multipart documents

4.2. WURFL usage

You can use WURFL by browsing the file as you would any other XML file and matching user-agent strings, but the wheel has already been invented, and on the same website where you can download the XML you will find APIs for the most common server platforms: Java, PHP, and .NET (in beta at the time of this writing). Generally speaking you should use the new APIs available on the website, but for compatibility purposes you can still find old APIs to download.

These APIs allow us to use WURFL in a couple of lines, with many advantages:

  • Automatic device detection using the header information

  • Two-step user agent analysis (optimized and clever user-agent searching inside the XML)

  • Detection of transcoders and proxies, and matching of the correct user agent and device information

  • Merging of the static XML provided by WURFL with patches (the web patch or your own), providing a simple and unique way to query capabilities

  • Caching of the XML parsing for the best performance on every request

4.2.1. PHP API installation

To use WURFL in PHP, you should download the PHP API from and extract the contents of the GZIP file. The package contains documentation, examples, resources, unit tests, and a WURFL folder where the API resides.


The PHP WURFL API allows us to save persistence and cache information using memcache instead of using the filesystem.

To make it work, follow these steps:

  1. Copy the WURFL folder into your web server root folder.

  2. Copy the resources or examples/resources folder into your web server root folder (you can change the name).

  3. Download the latest wurfl-<version>.zip file from the website and copy it to the new resources folder (along with the web_browsers_patch.xml file, if you need it).

  4. Create a cache folder inside the resources folder (or in another place, with a different name if you like) and verify that it PHP scripts have write permissions for this folder.

  5. Edit the resources/wurfl-config.xml file and check that the <main-file> tag matches the name of the ZIP file containing the main XML repository. It can also be a decompressed XML file.

  6. Edit the resources/wurfl-config.xml file, go to the persistence node, and check that the <params> tag matches the name of the cache folder, as in <params>dir=cache</params>. The path needs to be relative to the config XML folder.

Once WURFL is installed, we can create our first PHP script that uses the repository. Using version 1.0 of the API, the code will be:






In API 1.1, the objects were changed and WURFLManagerProvider was deprecated. So, the preceding code should be:

define("WURFL_DIR", dirname(__FILE__) . 'WURFL/');
require_once(WURFL_DIR . 'Application.php');

$wurflConfig=new WURFL_Configuration_XmlConfig($configFile);
$wurflManagerFactory=new WURFL_WURFLManagerFactory($wurflConfig);




If you run this file on your web server (local or remote), you will need to wait 1 or 2 minutes the first time while it creates the cache folder to enable quick detection in future requests. If you receive a blank page, great! If you get an error, you need to check all the steps again.


If you’re working on a local server and are going to upload your website to another server using FTP or some other protocol, it will be better to not upload the cache folder because it will contain thousands of files. It is better to leave the server to recreate them locally.

4.2.2. Using the PHP API

The PHP API is an object-oriented API. Once you have the WURFLManager object, regardless of whether you are using version 1.0 or 1.1 of the API, you can use it.


If your server is too loaded, you may get a timeout error when processing WURFL the first time. If this happens, ask your server provider how to increase the maximum script time limit or change the PHP.ini file.

A typical usage is getting a device object using the manager’s methods:

  • getDeviceForHttpRequest($_SERVER)

  • getDeviceForUserAgent($user_agent)

  • getDevice($deviceId)

If you want to access the capabilities of the current device accessing your website, the first option is the best one. If you want to get properties for other devices, you can use the user_agent method or the deviceId method. Every device in WURFL has an ID that we can store in our databases for statistics or logs. We can then look for its capabilities later, after the mobile request.


You can browse the WURFL devices database using the free online tool Tera-WURFL Explorer, available at

You can also get all the possible groups and capabilities using getListOfGroups() and getCapabilitiesNameForGroup(groupId), both methods of the manager.

Once you have the device object, you can get all properties with the getAllCapabilities() method or query for one particular feature with getCapability($capabilityName).

If you are using the desktop web patch, you can determine whether the client is a desktop or a mobile device:

if ($device->getCapability('is_wireless_device')==false) {
// It is not a mobile device

To detect if it is an iPhone or iPod Touch, use:

if ($device->getCapability('brand_name')=='Apple') {
// It is an iPhone, redirect to a prepared version

With the capability, you can then decide whether or not to provide some feature. For example:

if ($device->getCapability('xhtml_file_upload')=='supported') {
echo '<input type="file" />';

A great option if you are offering content is to explicitly display to the user his phone model in the marketing information:

echo 'Download compatible content for your ' .


If you want to test whether your WURFL code is working on your desktop browser, you can use Firefox and the free plug-in User Agent Switcher that allows Firefox to change its user-agent string to that of any other device of your liking.

4.2.3. WURFL-related products

A lot of related tools, utilities, and frameworks are available at These include:

  • Device Thumbnails (a repository of device images for thumbnails on websites)

  • Image Server (a Java servlet for dynamic conversion, scaling, and delivery of images to mobile devices)

  • Tera-WURFL (a PHP and MySQL implementation of the WURFL repository)

  • GAIA Image Transcoder

  • PHP Image Rendering Library (works with an old version of the PHP API)

  • Apache Mobile Filter


The Apache Mobile Filter is an open source solution for redirecting users to different versions using filters in an Apache module that looks into the WURFL database. It also supports image resizing. The project is available at

4.3. DeviceAtlas

In February 2008 (many years later than WURFL), the dotMobi company, which owns the .mobi top-level domain, launched its own device database that is similar in many ways to WURFL.

DeviceAtlas is a commercial product (with a free testing version for developers) available at that has partnerships with many data providers. According to dotMobi, this is not only the largest but also the most accurate device database on the market.

The main features are:

  • Monthly, weekly, daily, or constant updates to the database, depending on your license

  • A data explorer to browse the database from the Web

  • JSON data format support

  • APIs for PHP, Java, .NET, Python, and Ruby

  • Apache server module (with the enterprise license)

At the time of this writing, the basic license costs $99 per server per year and includes monthly updates but excludes the possibility to merge private data and other options available in higher license options.

4.3.1. Installation

You can apply for a free developer evaluation version at the website or buy a commercial version and then download the data and API from You will receive by email the license key, which is valid for one year. You will also receive the direct links to download the data file (in JSON format) in ZIP format.


The W3C is trying to standardize the device database repositories in the Device Descriptions Working Group, currently in draft at DeviceAtlas is offering its database in this new format as a preview.

You can get an automatic update of the JSON file using the URL<license>&format=zip (inserting your license key in the URL).

4.3.2. Properties

The data available in DeviceAtlas is segmented into categories. The most important properties per category are:

  • Device name: vendor, model

  • Hardware: displayHeight, displayWidth, mobileDevice, touchScreen

  • Environment: developerPlatform, developerPlatformVersion, osAndroid, osLinux, osOsx, osProprietary, osRim, osSymbian, osWindows, osVersion

  • Web browser: markup.xhtmlMp10, memoryLimitMarkup, uriSchemeSms, uriSchemeSmsTo, uriSchemeSmsTel, vCardDownload, usableDisplayWidth, usableDisplayHeight

  • Network protocols: EDGE, GPRS, HDSPA

  • JavaVM: cldc, jsr118, jsr139, jsr30, jsr37, midp

  • AudioPlayer: aac, amr, mp3

  • Streaming:, stream.3gp.h263, stream.3gp.h264.level1,

  • VideoPlayer: 3gp.h263, 3gp.h264.level1,, wmv

  • DRM: drmOmaCombinedDelivery, drmOmaForwardLock, drmOmaSeparateDelivery

You can browse all the data available with your license at


Remember that if you have a developer account your database file will not be updated if you download it again, and if you have a basic license with monthly downloads a new file will not be available until 30 days from when you downloaded the previous version.

4.3.3. PHP API

Inside the PHP API package you will find a doc folder containing PHPDoc documentation, a sample folder containing examples of usage, and a Mobi folder containing the API. The PHP API requires PHP version 5.2.3 with JSON support.

You need to copy the Mobi folder with all of its content into your website root, but you can put the JSON data file in any place you want. In your PHP file, you must include the file Mobi/Mtld/DA/Api.php.

A typical project will look like the following:

// We get a tree object loading the JSON
$tree = Mobi_Mtld_DA_Api::getTreeFromFile("deviceatlas.json");
// We get all the properties for the User Agent
$properties = Mobi_Mtld_DA_Api::getProperties($tree,
// Or we can get one property at a time using
$value = Mobi_Mtld_DA_Api::getProperty($tree, $ua, 'some-property');

If you want a cache implementation, you’ll need to do it yourself or have memcache installed on the server and save the entire tree as the sample provided by the API. The Java and .NET APIs have better support for caching techniques.

4.4. The ASP.NET Mobile Device Browser File

If you work with the ASP.NET platform, you can find an open source mobile browser database at, released by the Mobile Browse Platform Team at Microsoft. This file is attached to the current ASP.NET browser detection mechanism and is updated frequently. The sources of the information include WURFL, UAProf files, contributions from the community, and others.

To use it, all you need to do is download the mobile.browser file from the website, create a folder called App_Browsers (if you don’t already have one) with a mobile subfolder, and copy the downloaded file into that folder. That’s it!


The ASP.NET Mobile Device Browser File is only compatible with .NET Framework 2.0 and newer versions.

You can use the existing Request.Browser object in ASP to get information about the requesting device. IsMobileDevice will tell you whether or not it is a mobile browser, Platform will tell you the operating system, ScreenPixelsWidth and ScreenPixelsHeight will tell you the screen dimensions, and you can query any other property using Request.Browser as a Collection.

Here is an example in C#:

if (Request.Browser.IsMobileDevice) {
Response.Write("This is a " + Request.Browser.Platform + " device");
if (Request.Browser["SupportsTouchScreen"]) {
// It is a touch-based device

4.4.1. Capabilities

A full list of capabilities can be found at, but the most important are included in Table 5. There are capabilities for each video, audio, and image property.

Table 5. Common capabilities of the ASP.NET Mobile Device Browser File
CapabilityReturn classIndicates…
AcceptsImageSVGBooleanWhether the device supports SVG 1.1
AjaxCanManipulateCssBooleanWhether we can change CSS properties from JavaScript
AjaxSupportsFullDomBooleanWhether we can use full DOM methods
AjaxSupportsGetElementByIDBooleanWhether we can use getElementById from JavaScript
AjaxSupportsInnerHtmlBooleanWhether we can use innerHTML without problems
AjaxXmlHttpRequestConstructorSyntaxStringWhich syntax to use when creating an XMLHTTPRequest object (none for no Ajax support, standard for the native XHR object, or msxml2 for IE syntax)
InputTypeStringWhich input type is supported (keyboard, telephoneKeypad, or virtualKeyboard)
IsMobileDeviceBooleanWhether or not the current device is a mobile device
JavaScriptBooleanWhether the device supports JavaScript
MobileDeviceManufacturerStringThe device’s brand name
MobileDeviceModelStringThe device’s model name
PreferredRenderingMimeStringThe preferred MIME type for XHTML content
SupportedFlashVersionStringWhich Flash version is supported (none or the version number)
SupportsAccesskeyAttributeBooleanWhether the device supports the accesskey value
SupportsCssBackgroundImageBooleanWhether the device supports defining background images
SupportsEmbeddedFlashInWebPagesBooleanWhether the device supports embedding a SWF file
SupportsTouchScreenBooleanWhether the device is touch-based
SupportsWapPushBooleanWhether the device supports WAP Push
SupportsXhtmlRenderingBooleanWhether the device supports XHTML

4.5. Service-based solutions

You may not want to write all this yourself. Services are available to help; we’ll look at a few of them here.

4.5.1. Movila DetectFree

Movila Detection ( is a server-side Java solution to detect in 500 microseconds which device is using an embedded repository. It also works as a tool for URL rewrites. However, Movila’s most-used feature is the free service called DetectFree.

DetectFree is a free light version of the service available for PHP and JavaScript (and for any other platform that sends HTTP requests) that allows you to detect whether the connecting device is a mobile device. You can find samples and documentation at Just to illustrate how easy it is to use, the following sample is a JavaScript detection mechanism:

<script src=""

<script type="text/javascript">
if (is_mobile) {
alert("This is a mobile device");

4.5.2. DetectRight

DetectRight is a detection engine, device database, analytics engine, and API/SDK with both service-based and dedicated server options. Free noncommercial/developer licenses are available, and a shared-service license begins at 399 euros per month. It features SOAP and REST access, unique custom identification, country-level geolocation, and profiles in WURFL, DeviceAtlas, UAProf, DetectRight, and Java ME Polish–compatible formats. It features over 20,000 devices at the time of this writing.


If you want easy and quick mobile detection, at you will find a little PHP code that allows you to determine whether the user is using a mobile browser without any repository, database, or service call.

If you register at, you will receive via email a key that enables you to access a variety of PHP, .NET, and SOAP samples and APIs.

Remember that this is a service-based solution, so you don’t need to download or update any database on your server. Every request will be sent over the Internet to the DetectRight servers.


Remember that when using service-based solutions, your mobile website’s performance will depend on the reliability of the third-party server to which you are connecting.

You can download the PHP or .NET API for easy usage for those platforms.

In PHP, once you’ve downloaded the API you can use the service as shown in the following sample:


DetectRight::$druser = '< username>';
DetectRight::$drpassword = '< password>';
// possible values: DR, WURFL, W3C, UAProfile, J2MEPolish
DetectRight::$defaultSchema = 'DR';

$value = $profile['<property>'];

The API can also be used to download lists of manufacturers and handsets, and to request individual ones by manufacturer/model name.

  •  Using Windows Phone 7 Technologies : Retrieving Accelerometer Data (part 2)
  •  Using Windows Phone 7 Technologies : Retrieving Accelerometer Data (part 1)
  •  Using Windows Phone 7 Technologies : Understanding Orientation and Movement
  •  Programming the Mobile Web : HTML 5 (part 4) - Client Storage
  •  Programming the Mobile Web : HTML 5 (part 3) - Offline Operation
  •  Programming the Mobile Web : HTML 5 (part 2) - The canvas Element
  •  Programming the Mobile Web : HTML 5 (part 1)
  •  Windows Phone 7 : Submitting Your First Windows Phone Application to the Windows Phone Marketplace
  •  Windows Phone 7 : Packaging, Publishing, and Managing Applications
  •  Mobile Application Security : Windows Mobile Security - Development and Security Testing (part 3)
  •  Mobile Application Security : Windows Mobile Security - Development and Security Testing (part 2)
  •  Mobile Application Security : Windows Mobile Security - Development and Security Testing (part 1)
  •  Programming the Mobile Web : Mobile Rich Internet Applications (part 2) - JavaScript Mobile UI Patterns
  •  Programming the Mobile Web : Mobile Rich Internet Applications (part 1) - JavaScript UI Libraries
  •  Windows Mobile Security - Kernel Architecture
  •  Windows Mobile Security - Introduction to the Platform
  •  iPhone Programming : Table-View-Based Applications - Building a Model
  •  Mobile Application Security : The Apple iPhone - Push Notifications, Copy/Paste, and Other IPC
  •  Mobile Application Security : The Apple iPhone - Networking
  •  Windows Phone 7 Development : Handling Device Exceptions
    Most View
    Programming Excel with VBA and .NET : Variables (part 4) - User-Defined Types & Objects
    OpenGL on Windows : Full-Screen Rendering, Double Buffering
    iRig Mix - Party Rock Is In The House Tonight!
    Apple Releases OS X Mountain Lion Developer Preview 4
    Programming .NET Components : Remoting - Application Domains (part 1)
    Adobe Flash Catalyst CS5 : Wireframing an Application in Flash Catalyst - Run a Project
    Visual Studio 2010 : Writing Custom Facebook Applications - Querying Data from Facebook
    What To Do With An Old Mac (Part 4)
    Creative Sound Blaster Tactic3D Alpha Headphones Review
    Android 4.0 Ice Cream Sandwich Guided Tour (Part 3)
    Top 10
    Kingston Wi - Drive 128GB: Simple To Get Started
    Seagate Wireless Plus 1 TB - Streaming Videos To Various Devices
    Seagate Wireless Plus 1TB - Seagate's Second Wireless External Hard Drive
    Western Digital My Passport 2TB - The Ideal Companion For Anyone
    Lenovo IdeaTab A2109 - A Typical Midrange Android Tablet
    Secret Tips For Your Kindle Fire
    The Best Experience With Windows 8 Tablets And Hybrids (Part 2)
    The Best Experience With Windows 8 Tablets And Hybrids (Part 1)
    Give Your Browser A Health Check
    New Ways To Block Irritating Ads…