Controlling Focus in Firefox

November 24th, 2010

I have a bunch of JavaScript unit tests and for some of them I need to trigger a real keypress – one that activates the browser’s default functionality, not just triggers the JavaScript listeners. To do that, I have a simple signed applet that uses the AWT Robot to programmatically trigger the keypresses. Mostly, this works brilliantly.

The problem I’m having is that about 1 time in 10, the focus goes AWOL and the key events generated by the robot simply aren’t delivered anywhere. It’s generally not reliable, though I have got a setup now where I can make it happen within a few minutes of running the tests on a loop.

The problem is specific to Firefox 3.6 – I’ve never seen it fail on anything else. I’m testing against Firefox 2.0, 3.0, 3.6 and 4.0 beta 6. 3.6.3 and 3.6.12 to be precise. It fails on all the OS’s I’ve tried: Linux (Ubuntu 10.10), Mac OS X (10.6) and Windows Vista.

The Simple Question

Is there a sure-fire way in JavaScript to force focus to a specific element?

The normal Element.focus() doesn’t seem to be strong enough as I’m already calling that (and I’ve tried oh so many variations of it). I’m even ok with requiring that element to be of a specific type (e.g. a text input). JavaScript is ideal but if it can be done with a signed Java applet I’m ok with that too. Config options, browser plugins and maybe even native code would all be a possibility.

The Simple Answer (Update)

It turns out that the ineffectiveness of Element.focus() is a usability/security feature of Firefox which is configurable. In the “Content” tab of preferences, click the “Advanced…” button beside JavaScript and then ensure that “Raise or lower windows” is checked. Annoyingly simple after the amount of effort it took to work out what was going wrong.

Firefox advanced JavaScript preferences window

The Detail

Here’s what I know:

  • The test runner framework loads the actual test pages inside an iframe.
    • The signed applet is inside this iframe.
  • The test that is giving me trouble uses TinyMCE, which itself uses an iframe, so we have an iframe inside an iframe and that’s where we need to get focus.
  • To make sure that the actual browser pane has focus (rather than the URL bar etc), when the page first loads the applet clicks just above itself (thus moving focus to the actual test page inside the first iframe).
    • I can watch this happen so I know the click is in the right place.
  • The page is definitely fully loaded before the problematic test runs. It’s not the first test in that file but it is the first test that actually uses the robot (the click still happens on page load). I can’t remember a failure where it wasn’t the first test using the robot that failed but can’t be 100% certain of that.
  • I’m using the JavaScript focus() method to move focus to the element I’m targeting.
    • I’ve found I need to focus the main window, then each nested iframe window and document body and then finally the element I want to target to make sure that the focus is on the right frame.
    • I can check that document.activeElement is pointing to the right place.
  • When the focus gets lost, even manually typing keys does nothing.
    • But typing tab will move the focus back to the right spot and then typing the key the test was expecting will make the test pass correctly.
  • The key event is definitely firing from the AWT robot, because if that key is the tab key, it correctly moves the focus back to where it should be (but I have no way to know programmatically that I need to repeat the keystroke).

Why Not Selenium?

Just to cover this off, there are a few reasons that this project isn’t using Selenium:

  1. It was an existing project which already had tests using QUnit and most of the tests don’t need this Robot functionality so can be done in pure JS.
  2. It’s a pure JS project so it’s really nice to also write the tests in JavaScript (the robot has a nice JS wrapper).
  3. This approach lets me run the tests in any browser without requiring any setup at all. No need to install and configure anything – you could very likely walk up to a computer in an internet cafe and have the tests pass which makes life so much easier.
  4. It should work damn it! I want to know why it doesn’t and why Selenium, which also uses the AWT Robot doesn’t hit the same problem (or maybe it does?).

Murphy’s Law

Just before starting to write this post (some 30 minutes ago due to distractions), I had the crazy idea to add a java.awt.TextField to the applet and make the applet a little bit bigger (150x40px instead of 10x10px). The tests have been running continuously with that new configuration since then without a single failure. The text area is never used in any way, but for some reason just having it there seems to make life better. I don’t get it.

Due to a power outage I can’t run it on the actual build servers until tomorrow so I’m going to post this anyway since you just know it will start failing again just as soon as I think I’ve got it fixed.

Update 10 seconds after posting: Yep, there it goes, failed again.

JavaScript Performance: For vs ForEach

October 9th, 2010

As part of exploring JavaScript performance, I started wondering if the particular method of looping made a difference to performance. After all, one of the most often repeated bits of advice is that JavaScript is much faster if you loop backwards through an array instead of forwards and the slow part of my code was a loop. Using jsperf.com I could set up a simple test case pretty quickly and easily: for vs forEach.

There are a bunch of variants on loops in that test and the results really surprised me:

Browser for loop, cached length for loop, cached length, no callback for loop, cached length, using function.call for loop, reverse for loop, simple forEach # Tests
Chrome 4000 4531 2184 3804 3333 2998 8
Firefox 6928 7676 4499 8192 7211 450 6
iPad 82 95 69 77 71 103 1
iPhone 87 111 70 88 84 119 1
Safari 3382 5029 1816 3678 3122 1942 4

Here’s that data in a graph for Chome and Firefox just to make it a bit easier to visualise:

Coloumn chart of Chrome test results

Coloumn chart of Firefox test results

Unsurprisingly, the performance characteristics vary quite noticeably between browsers. What was surprising to me at least was that the new [].forEach() is one of or the slowest method to iterate through an array – slower than implementing forEach yourself in JavaScript in most browsers (mobile Safari being the exception).

The other thing that stood out is that green bar in the middle, representing the ‘for loop, cached length using function.call’ which is the current method of looping I’m using because that what the libraries implement. The advantage of this approach is that it not only supplies each item in the array, it also gives the index and the full array as extra arguments and it allows me to specify an object to use for ‘this’ in the callback function. That’s all potentially very handy but I wasn’t actually taking advantage of it. Looks like switching to a custom ‘each’ implementation which uses the for loop with cached length approach should give a performance boost without making any other changes to the code so no sacrifice in maintainability.

Sounds great, but after making the change there was less than a millisecond difference in the actual performance – well within the margin for error for measuring time in JavaScript performance tests.

Lessons Learnt

  • Use whatever for loop setup you want – it’s unlikely to affect overall performance in any noticeable way.
  • It’s probably not worth adding code to detect if there’s a built-in forEach you can leverage – just use the JavaScript implementation you’d need on older browsers anyway.

The Remaining Question

It’s all very well to learn these lessons, but since I’ve done all of this playing around in my own time at home, there’s a very significant problem with the results: they don’t include IE1. Since my real performance problems are worst on IE 6,7 and 8, I need to be running these tests there – it’s possible that changing the looping system really would yield performance gains in IE. I’ll have to try that out on Monday.

JS Array .filter

1 – I do have IE in a VM I’ve just been too lazy to fire it up. Of course, if you happen have IE ready to hand, you can go and run the tests yourself and jsperf.com will add that into the mix

Optimising JavaScript

October 7th, 2010

I’ve been working to build a better filter for pasting from Microsoft Word into TinyMCE and if you know anything about filtering what Word calls HTML, you know there’s an awful lot of work to be done. As such, performance becomes a real issue – ideally we’d like to be able to filter reasonable sized documents (10-20 pages) in under the half a second that users generally call snappy.

Since filtering Word HTML is also inherently complex and we want to ensure the code is maintainable, I started out by developing the code in the most maintainable way possible, without regard to performance. There’s also a lot of tests to ensure correctness, and allow me to optimise later without fear of breaking things. With that done, it’s time to set up some performance tests and see what kind of performance behaviour it has.

Approach

A couple of notes here, the performance I’m testing is all about the filtering, so while it is making a lot of use of the DOM to parse the HTML and give me some structure to filter it, it doesn’t render anything on screen at all. I’m also not yet concerned about the actual time taken, instead focussing on identifying which areas are relatively slow and whether or not optimisations have any actual improvements. I will probably establish some performance tests where the actual timings matter to prevent future changes from slowing things down too much, but for now the best test I can do to get a sense of “is it fast enough?” is to actually paste content from Word manually. After all, my aim is only for users to perceive it as fast.

Quick Lessons Learnt

I will write up some of the more detailed lessons separately, but here’s a few quick observations1:

  • IE9 is a massive improvement in performance. The IE team deserve a huge pat on the back for what they’ve achieved. Performance was close enough to Safari and Chrome for it not to matter.
  • Firefox isn’t slow but it’s certainly not fast yet. They’re putting in a big effort though so I have high hopes for the final Firefox 4, or worst case 4.5.
  • Performance bottlenecks can be more surprising than you think. From jsconf.eu I knew that DOM access was going to be a bottleneck but what surprised me is that iterating attributes is dramatically slower than iterating styles.
    • Despite that, it was still faster to retrieve style.cssText and use string parsing to access style values rather than using the .style properties directly2.
  • Parsing HTML by setting innerHTML is really fast across the board, even with quite large documents.
  • jsperf.com is awesome for micro-benchmarks. I need to use it a lot more.

Most Important Lesson

The biggest thing I’m taking away from the experience so far is that performance is unpredictable and very unique to your situation. You need to use performance tests and keep focusing on the results for your actual use-cases. Micro-benchmarks and other people’s benchmarks can lead you astray – both in terms of potentially hurting your performance and definitely hurting maintainability. Cargo-cult optimisation simply doesn’t work.

JavaScript String .valueOf

1 – Note that the browser comparisons here are somewhat unfair – the tests aren’t running in a controlled enough environment to really compare different setups but I’m confident they are accurate enough to speak in these broad terms at least.

2 – the code to do this was required anyway since in Safari non-standard styles are available via getAttribute(’style’) but not via the parsed .style attribute, and we need access to those non-standard styles to perform the filtering properly.

Canvas-based Editors

September 29th, 2010

Over the weekend I went to JSConf EU and every time I met someone and told them I was working on TinyMCE the conversation rapidly veered off into why contentEditable is too hard/buggy to work with and how it would be so much better if it was all built on top of canvas or just used the DOM with a custom selection API. This matched the thinking throughout the conference that it doesn’t really matter how messed up browsers are because JavaScript can be used to work around it.

What disturbs me is that incredibly few people realise just how much work is involved in implementing caret and selection handling correctly, let alone most of the other infrastructure provided by contentEditable. I wound up talking to Mark Wubben who actually does have a pretty good idea – he implemented a DOM based selection system for the Xopus editor. Needless to say he had quite the war story to tell, but despite doing what sounded like a very impressive bit of work, the selection code is still very broken in a couple of key ways: accessibility and international inputs1.

Accessibility

Accessibility support was my first response to anyone suggesting using canvas as an editor – no-one had considered it. If you play around with a screen reader for a while, you start to realise just how important the selection is to accessibility. Selecting text generally causes it to be read out for example. It’s not just screen readers either, screen magnifiers will move the screen along to keep up with the caret, ensuring the user can see what they’re typing. If the caret is fake, none of these features will work and the editor becomes inaccessible.

International Inputs

Even I’d forgotten about this one, but it’s likely an even bigger problem to solve than accessibility. The array of input devices, keyboard shortcuts and other ways that people input characters is pretty amazing when you start exploring it. People in English speaking countries don’t tend to notice this because they have quite a small number of characters in use which all fit on the keyboard. Even Western European countries have the vast majority of characters required available with a direct key sequence. However there are far more complex types of inputs around. The simplest of these is the use of “dead keys” such as acute on a US or British keyboard. To type é actually takes multiple keystrokes on a UK Mac keyboard:

  1. Option-e. This is a dead-key so only a placeholder appears in the output (Placeholder for acute shown with a yellow background.).
  2. e. Now the e-acute character actually appears and is inserted into the document content.

Not to pick on Xopus but at least in Firefox on Mac, I can’t type é at all in the editor because it doesn’t correctly support dead-keys2.

Asian languages can have even more keystrokes required to type a character and much more complex input methods that to be usable, require changes to the caret, underlining content etc. Plus the methods and UI is different on different OS’s. While users can handle some variation, they shouldn’t have to learn a whole new input method to be able to use your application3.

The Way Forward

Instead of trying to work around the limitations in contentEditable we need to be pushing browser vendors to fix it and make it usable. Where the current APIs aren’t good enough, standardise better ones – we’re dealing with multiple incompatible APIs already so the backwards compatibility problems are limited. This needs to be a focus so that we don’t wind up in a world where online editors only work for fully-abled, English speaking users. Most if not all of what’s required is already there, it just needs some love and care to remove the bugs and better handle corner-cases.

1 – There is some good discussion of the complexities involved here from the W3C canvas list as they struggle with the accessibility implications of canvas.

2 – and to be completely fair, EditLive! fails to paint the placeholder, though the character does type correctly.

3 – So no, Google Virtual Keyboard is not a real solution, however good an idea it is for people using things like internet kiosks with foreign keyboard layouts. Besides, would you like to be stuck typing only with your mouse?

contentEditable in Mobile WebKit

September 13th, 2010

What Is contentEditable?

JavaScript editors are all fundamentally based on a technology called contentEditable which makes a part of the web page become editable by the user. While there is a lot of functionality that’s usually bundled under the “contentEditable” title, there are a few core parts which are essential for a browser to support correctly1:

  • Selection – allowing the user to position a text caret in the content and select various parts of it.
  • Content entry and deletion – allowing the user to type basic text and to delete selected content.

Without these two key components of contentEditable it’s impossible to create a high-quality, WYSIWYG editor with JavaScript. Despite some variations in how things work, this functionality is available on all common desktop browsers and has been for quite a few years.

The Problem

On mobile WebKit, for both iOS and Android, the basics of contentEditable support aren’t working correctly. The problem is two fold:

  1. While the user can position a text caret and change the selection, the actual caret (the blinking vertical line), never renders so users don’t know where their typing will appear.
  2. The on-screen keyboard doesn’t appear when the caret is placed in an editable section. On devices without a real keyboard it’s then impossible to type.

WebKit is actually surprisingly close to getting this working, with a lot of the underpinning functionality appearing to work but these two issues prevent editors from being usable2.

Why Does It Matter?

While geeks may feel at home writing HTML code by hand or using a markup language like markdown or textile, non-technical users find it significantly easier to use a WYSIWYG editor. Currently, it’s not possible to offer such an editor on mobile devices due to the lack of contentEditable support.

Even worse is that many sites are already using a WYSIWYG editor which then prevents users on a mobile devices from contributing content – even when the rest of the site works perfectly. With mobile browsers improving so rapidly, most sites now work without needing special adjustments so the lack of contentEditable support is one of the few major incompatibilities.

How Might it be Fixed?

To be honest, I don’t know and don’t have the experience with the code involved to find the source of the problem and fix it. However, with both the source to WebKit and Android being open, hopefully someone does have the experience knowledge and a bit of time to solve this.

With WebKit being the browser engine of choice on so many devices, the fix may even flow around to a bunch of mobile OS’s.

Relevant Bug Reports

Android

iPhone

Apple don’t provide a public bug list, but this issue is logged as Radar #8399288. Apple do use the number of bug reports for an issue to adjust priority so if this issue is important to you, please do log an issue and cite Radar #8399288.

Others

I don’t currently have bug reports against other systems but would appreciate pointers to any relevant bugs for other systems (whether they use WebKit or not) so they can be tracked.

1 – The other major component is the execCommand function which provides built-in implementations of a number of common commands like bold, italic, etc. While it’s certainly useful to have these, they can generally be replaced by pure-JavaScript implementations without sacrificing quality or performance.

2 – I’m unaware of any mobile browser that supports contentEditable well enough to make an editor feasible. In fact, WebKit appears to be the closest to that milestone.