Posts Tagged: Internet Explorer


26
Jun 07

Detecting plugins in Internet Explorer (and a few hints for all the others)

There’s a lot of bad information out there about detecting plugins in Internet Explorer. I know; I spent days crawling through it all in order to create a plugin detection tool for a client. It’s not that the code you’ll find in the crevices of those forgotten pages doesn’t work. It does. The problem is that virtually all of it is grossly inefficient and almost immediately outdated because it’s version-based and has to be updated with each new plugin release. On top of that, some of it still uses VBScript, or even worse, JavaScript that writes VBScript. Since plugin detection itself continues to be necessary in some unique situations, I was determined to find a way to do it that didn’t require constant maintenance.

The problem is that no one quite knows what to look for when dealing with Internet Explorer’s plugins. Plugins in IE are ActiveX-based, so there’s no single API for them all—each has its own method of returning, for example, version information. What this basically means is that while Firefox and other browsers are putting all of their plugin information in one handy place, Internet Explorer is jealously guarding its plugin-related secrets. It’s like the Dick Cheney of browsers. And we are, um, the Information Security Oversight Office in that analogy.

Er… let’s just jump right in with some working detection code.

Adobe Reader

Detecting Adobe Reader (formerly Acrobat Reader) is pretty straightforward, except for the hitch that the ActiveX control was renamed in version 7. The key for version detection here is GetVersions(). Unfortunately, no version information is available for Adobe Reader 8 and later for non-IE browsers.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
  var control = null;
  try {
    // AcroPDF.PDF is used by version 7 and later
    control = new ActiveXObject('AcroPDF.PDF');
  } catch (e) {
    // Do nothing
  }
  if (!control) {
    try {
      // PDF.PdfCtrl is used by version 6 and earlier
      control = new ActiveXObject('PDF.PdfCtrl');
    } catch (e) {
      return;
    }
  }
  if (control) {
    isInstalled = true;
    version = control.GetVersions().split(',');
    version = version[0].split('=');
    version = parseFloat(version[1]);
  }
} else {
  // Check navigator.plugins for "Adobe Acrobat" or "Adobe PDF Plug-in"*
}

* Nicole Lucas adds that “Adobe Acrobat” won’t work with Adobe Reader 8 because Adobe changed the name to “Adobe PDF Plug-In for Firefox and Netscape”. Thanks, Nicole.

Flash Player

Flash is even easier. The version method in this case is GetVariable('$version').

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
  var control = null;
  try {
    control = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
  } catch (e) {
    return;
  }
  if (control) {
    isInstalled = true;
    version = control.GetVariable('$version').substring(4);
    version = version.split(',');
    version = parseFloat(version[0] + '.' + version[1]);
  }
} else {
  // Check navigator.plugins for "Shockwave Flash"
}

Java Runtime Environment

The JRE (formerly Java Virtual Machine, or JVM) is actually more difficult to handle than you would think. Determining if Java is installed is easy—a quick call to navigator.javaEnabled() returns a simple Boolean.* The problem is detecting the version and provider (Microsoft or Sun).

I never found a satisfactory solution to this. The gist is this: to get this information, you must load a Java applet. To load the Java applet, you must do it externally. But when an applet is loaded externally, it doesn’t load right away, so any programmatic calls to the exposed class methods must be delayed. And dealing with this with setTimeout() is awkward.

Nevertheless, here’s what I ended up with:

var isInstalled = navigator.javaEnabled();
if (!isInstalled) {
  return;
}

// Get version
var version = null;
if (/*@cc_on ! @*/ false) { // This JRE check does not depend on ActiveX, only IE
  // IE requires the 'classid' attribute upon object creation.
  var element = '<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93">';
  var applet  = document.createElement(element);
  applet.id = 'applet';

  // IE requires that objects be displayed in order to interact with them
  applet.style.width  = '1px';
  applet.style.height = '1px';

  // Create and append the object's 'code' parameter
  var param = document.createElement('param');
  param.name  = 'code';
  param.value = 'JavaDetector.class';
  applet.appendChild(param);

  // Append the applet to <body>
  document.body.appendChild(applet);

  // Attempt to get the version
  var window.java = new Object();

  // Synchronous calls to getVendor(), etc. result in an exception, but despite
  // the timeout of 0 this call will not execute immediately
  setTimeout(function() {
    try {
      var applet = document.getElementById('applet');
      window.java.vendor  = applet.getVendor();
      window.java.version = parseFloat(applet.getVersion());
    } catch (e) {
      // Do nothing
    }
  }, 0);
} else {
  if (typeof java != 'undefined') {
    version = java.lang.System.getProperty('java.version');
  } else {
    // Look for "Java Plug-in", "Java (version) Plug-in", and "Java Plug-in for Cocoa"
  }
  version = parseFloat(version);
}

And your Java class looks something like this:

public class JavaDetector extends java.applet.Applet {
    public static String getVendor() {
        String vendor = null;
        try {
            vendor = System.getProperty("java.vendor");
        } catch (Exception e) {
            vendor = "Microsoft Corp.";
        }
        return vendor;
    }

    public static String getVersion() {
        return System.getProperty("java.version");
    }
}

* Eric Gerds adds that “navigator.javaEnabled() does not reveal whether Java is installed or not in IE.” Apparently disabling Java in IE merely disables the applet tag. Applets can still be loaded with the object tag despite javaEnabled() returning false. As such, you may want to modify this code. See his complete comment below.

QuickTime Player

QuickTime is another one that poses difficulty for IE. Detecting it is simple enough: just check for QuickTime.QuickTime. But detecting the version poses other problems. The ActiveX control that returns version information for QuickTime must be enabled manually in Internet Explorer 7. When called, the user is prompted. Although it saves that information for all future visits to that page, the code that follows will typically return null for the version number the first time around. Unfortunately, I’ve found no good way to reliably detect the version through ActiveX alone.

The version number that you do receive isn’t immediately useful. The QuickTimeVersion property is in hexadecimal (for whatever reason), so you’ll need to convert it by calling QuickTimeVersion.toString(16) and then manually assembling the number.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
  var control = null;
  try {
    control = new ActiveXObject('QuickTime.QuickTime');
  } catch (e) {
    // Do nothing
  }
  if (control) {
    // In case QuickTimeCheckObject.QuickTimeCheck does not exist
    isInstalled = true;
  }

  try {
    // This generates a user prompt in Internet Explorer 7
    control = new ActiveXObject('QuickTimeCheckObject.QuickTimeCheck');
  } catch (e) {
    return;
  }
  if (control) {
    // In case QuickTime.QuickTime does not exist
    isInstalled = true;

    // Get version
    version = control.QuickTimeVersion.toString(16); // Convert to hex
    version = version.substring(0, 1) + '.' + version.substring(1, 3);
    version = parseFloat(version);
  }
} else {
  // Check navigator.plugins for "QuickTime Plug-in"
}

RealPlayer

Because it’s had so many names (five, that I can count), RealPlayer is still one of the more inefficient plugins to check for. Thankfully, they’ve kept the API the same, however, so checking for the version is still just GetVersionInfo().

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
  var definedControls = [
    'rmocx.RealPlayer G2 Control',
    'rmocx.RealPlayer G2 Control.1',
    'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)',
    'RealVideo.RealVideo(tm) ActiveX Control (32-bit)',
    'RealPlayer'
  ];

  var control = null;
  for (var i = 0; i < definedControls.length; i++) {
    try {
      control = new ActiveXObject(definedControls[i]);
    } catch (e) {
      continue;
    }
    if (control) {
      break;
    }
  }
  if (control) {
    isInstalled = true;
    version = control.GetVersionInfo();
    version = parseFloat(version);
  }
} else {
  // Check navigator.plugins for "RealPlayer" and "RealPlayer Version"
}

Shockwave Player

Thankfully, Macromedia always made an effort to remain consistent with their naming. Thus, one simple call can detect any version of Shockwave. The version check here is ShockwaveVersion('')—yes, you must pass it an empty string.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
  var control = null;
  try {
    control = new ActiveXObject('SWCtl.SWCtl');
  } catch (e) {
    return;
  }
  if (control) {
    isInstalled = true;
    version = control.ShockwaveVersion('').split('r');
    version = parseFloat(version[0]);
  }
} else {
  // Check navigator.plugins for "Shockwave for Director"
}

Windows Media Player

Windows Media Player is probably the easiest one to detect of them all, but also the most unreliable on non-Internet Explorer browsers. Firefox users will typically be missed here unless they’ve installed the WMP for Firefox plugin. The relevant version property is versionInfo.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
  var control = null;
  try {
    control = new ActiveXObject('WMPlayer.OCX');
  } catch (e) {
    return;
  }
  if (control) {
    isInstalled = true;
    version = parseFloat(control.versionInfo);
  }
} else {
  // Check navigator.plugins for "Windows Media"--this also detects the Flip4Mac plugin
}

Phew! That’s that. I can’t guarantee these are the best methods of detecting plugins in Internet Explorer, but they’re the best I came up with. If you’re aware of easier solutions, let me know and I’ll update this post.

Incidentally, if you need a comprehensive solution, I recommend putting these into a class structure—for my project, I used an abstract base class that each plugin class extended, and an overall plugin detection class that called each class’s self-detection method. Extensible, self-contained, and unobtrusive.

Like this post? You might also like Coalmine, my centralized error tracking service for your apps. Coalmine captures errors and all kinds of helpful debugging information, notifies you, and makes it all searchable. Check it out!

4
Dec 06

Different IEs, same machine

IEBlog posted an article last Thursday on running multiple versions of Internet Explorer for testing purposes. Their suggestion? Virtualization using a downloadable Virtual PC image. Yikes.

Of course, an easier and more realistic way that designers and developers have relied on for over two years is using evolt.org’s standalone versions of IE, based on discoveries made by Joe Maddalone. Even better, download Yousif Al Saif’s self-extracting installer, which lets you conveniently select which versions of Internet Explorer you want to install.

The good news is that now that IE 7 is out in the wild via Windows Update, we can safely begin to ignore IE 5 and 5.5 and focus exclusively on 6 and 7. Frankly, Internet Explorer 8—and with it, the death of 6—can’t come soon enough.

Like this post? You might also like Coalmine, my centralized error tracking service for your apps. Coalmine captures errors and all kinds of helpful debugging information, notifies you, and makes it all searchable. Check it out!

23
Oct 06

A fix for Suckerfish dropdowns in IE 7

Internet Explorer 7 is a welcome upgrade from IE 6, but it still has its share of display issues—and some new ones besides. Recently, I ran across one that falls in the latter category. Although Patrick Griffiths and Dan Webb’s Suckerfish dropdowns (and their follow-up, Son of Suckerfish) work fine in IE 6, they more or less choke and die in IE 7.

The problem is this: if you change focus within the browser (by clicking in a text field, for example, or somewhere on the background), then move back across the dropdowns, you’ll notice that all the menus “stick”—they don’t go away. In other words, they don’t refresh when the mouseout event is fired, even though the special hover class is removed.

Luckily, the fix for this is pretty simple. Assuming your unordered list menu ID is “menu”, you just need to add the following code to your stylesheet:

#menu li:hover, #menu li.hover {
  position: static;
}

Voilà, he says with proper grave accent! Problem fixed.

Like this post? You might also like Coalmine, my centralized error tracking service for your apps. Coalmine captures errors and all kinds of helpful debugging information, notifies you, and makes it all searchable. Check it out!