Fixing Firefox Flash Foolishness

| tags: blind, motor impaired, programming, enabling technology

Firefox is the only browser I use, so when things don't work, I get worried. We're looking at Flash as a delivery vehicle for some of our applications for people with disabilities and ran into two potential show stoppers:

  1. Flash doesn't get focus unless you click on it with the mouse; many of our potential users don't use mice!
  2. Flash doesn't allow access to right click; many switch interfaces generate left and right click events to signal the user pressing the mover or chooser switch.

I surfed around a bit expecting to find some quick solution and only found despair. It seems that lots of people have encountered these problems in various forms and haven't found a solution.

I've found a very simple approach that works just fine for what we need; perhaps it will help someone else. The high bit is I'm using the Flash ExternalInterface object to allow JavaScript to catch keyboard and mouse events and then tell Flash about them. Flash never gets the focus and doesn't need it.

I have attached an archive with the various pieces. If you download this and activate the HTML your browser may pop up a Flash security dialog; if so, you'll have to allow Flash to access the folder containing the code, or put it all on a web server someplace to get around the security model.

The Flash movie is just a trivial demo. I created a Dynamic Text control on the stage and gave it the id MyText. On Frame 1 I inserted the following ActionScript code:


import flash.external.*;

function showMsg(txt) {
	MyText.text = txt;
}
var connection = ExternalInterface.addCallback("callMe", this, showMsg);
if(connection) {
  showMsg('ExternalInterface ok');
} else {
  /* this apparently happens when the security model is violated */
  showMsg('ExternalInterface failed');
}

You can export as many different functions as you like with ExternalInterface.addCallback. Of course, you'd likely want different functions for keyboard and mouse events in a real application.

In the HTML I have some JavaScript that:

  1. catches keydown and keyup events,
  2. catches mousedown and mouseup events,
  3. informs the Flash movie about them using the function exported above, and
  4. disables the right-click context menu.
    1. Here is the HTML with the JavaScript embedded.

      
      <html>
      <head>
      <script type="text/javascript">
      var myMovie;
      function killEvent(eventObject) {
          if (eventObject && eventObject.stopPropagation) eventObject.stopPropagation();
          if (window.event && window.event.cancelBubble ) window.event.cancelBubble = true;
          if (eventObject && eventObject.preventDefault) eventObject.preventDefault();
          if (window.event) window.event.returnValue = false;
          if(eventObject.preventCapture) eventObject.preventCapture();
          if(eventObject.preventBubble) eventObject.preventBubble();
      }
      function keyCallback(msgtxt) {
        return function(ev) {
          var kcode = ev.which;
          var kchar = String.fromCharCode(kcode);
          var txt = msgtxt + ' ' + kcode + ' ' + kchar;
          myMovie.callMe(txt);
        }
      }
      function mouseCallback(msgtxt) {
        return function(ev) {
          var txt = msgtxt + ' ' + ev.button;
          if (ev.button != 0) killEvent(ev);
          myMovie.callMe(txt);
        }
      }
      function setup() {
        window.addEventListener("mousedown", mouseCallback('mouse down'), true);
        window.addEventListener("mouseup", mouseCallback('mouse up'), true);
        window.addEventListener("keydown", keyCallback('key down'), true);
        window.addEventListener("keyup", keyCallback('key up'), true);
        window.addEventListener("contextmenu",killEvent, true);
        myMovie = document.fev1;
      }
      </script>
      </head>
      <body onload="setup()">
      <embed src="fev1.swf"
      	quality="high"
      	bgcolor="#ffffff"
      	width="550"
      	height="400"
      	name="fev1"
      	align="middle"
              wmode="transparent"
      	allowScriptAccess="always"
      	swLiveConnect="true"
      	type="application/x-shockwave-flash"
      	pluginspage="http://www.macromedia.com/go/getflashplayer" />
      </body>
      </html>
      
      

      I emphasized what I think are the critical bits for the ExternalInterface in red and for killing the right-click context menu in blue. The rest is pretty straightforward JavaScript code. I won't be surprised if someone who knows Flash and JavaScript can do this much better.

      When you run this, you should see "External Interface OK" in the Flash movie; if you don't then you've got to figure out your security settings to allow the ExternalInterface object to properly initialize. Assuming you got past that, typing should show you the key code and letter on both key down and key up. Likewise, clicking with the mouse should show which button you're pressing and should allow you to click anywhere in the window without popping up either the Firefox or Flash context menus.

      I think this approach could be made to work in IE. Besides the usual differences in accessing the DOM, turning off the right-click context menu is likely the only part that will be different. I've seen suggestions of using a transparent shield layer over the Flash movie to catch those events.

      That's really all there is to it. I'd be very pleased to hear about improvements to this method. I suspect that a Flash programmer truly skilled in the arts could synthesize Key and Mouse events so that normal listeners would be invoked. If you do that, I'd love to learn how!