This mail contains far more detail than most readers would want to go into, but more than anything provides a repository for future reference and analysis should we ever run into an issue similar to this again (or indeed, as seems likely, continue running into *this* issue :P)

Over the last few of days, Yura and I have been investigating issues involving popup areas (in particular NumberPatternChooser and Autocomplete) that have been behaving incorrectly under IE8. This involves tracking of focus and blur events in a way that is consistent across all browsers and is usually unrewardingly intricate. We have two main components in CSpace which deal with this, according to different strategies. Actually until yesterday I wasn't clear that these were really aimed at essentially the *same* purpose. The first, "GlobalDismissal", is something fairly quick & simple I put in during my first month on CSpace in August to deal with cases where various dialogs (date widget, confirmation etc.) were not being dismissed correctly when the user clicked outside them. This initial implementation only tracked mouse events and was not extended to deal with general focus loss. One idea we had during the attempt to fix CSPACE-3304 was to extend "GlobalDismissal" by using the new jQuery synthetic event "focusin" which promises to allow registering of focus handlers which bubble up through the document (browsers historically have treated this bubbling inconsistently)


"GlobalDismissal" approach

In this approach we register a global "focusin" handler on the entire document base. In this case the popup opens and stays open, but unfortunately on IE8 closes on receiving the first down arrow keystroke (on all other browsers it behaves correctly). The following trace shows the sequence of focus and blur events.

*1) LOG: 17:54:20.375:  Invoking blur on 
TR#fluid-id-334.csc-numberPatternChooser-patternRow cs-selecting
*2) LOG: 17:54:20.390:  received focusin on BODY#acquisition
*3) LOG: 17:54:31.906:  Invoking focus on 
TR#fluid-id-335.csc-numberPatternChooser-patternRow
*4) LOG: 17:54:32.172:  received focusin on BODY#acquisition

The transition between *1 and *2 is apparently synchronous. This means that with the "globalDismissal" approach the popup will already have been closed by the time the focus event arrives (these were actually from two different keystrokes, please ignore the intervening gap of 11 seconds)

The initial "blur" results from the behaviour of the jquery.keyboard-a11y plugin which attempts to ensure consistency by matching synthetic blur events to focus events. Unfortunately IE8 *synchronously* interprets the blur event without waiting for a further focus on the same event stack, and immediately synthesises its own focus based on the blur which focuses "nothing in particular", in fact the entire document body. This implies that any "stateless" approach to tracking blur on IE8 cannot work (unless the a11y plugin is rewritten to not generate blurs particularly on IE8 - they are unfortunately necessary on at least two other modern browsers, FF 3.6 and Safari 4) since this intervening event will *always* be interpreted as a signal that the contents of the popup has lost focus.

"Dead Man's Blur" approach

This is a utility which has been unofficially part of the Fluid framework for a couple of releases but in the current 1.3 release is being upgraded to core status - especially in regard of its usage in several places around CSpace. This registers a "stateful event processor" which, on receiving a blur on some number of "core elements" starts a timer, waiting to see whether the reason for the blur is a shortly forthcoming focus on ANOTHER of these elements. If this focus is received, the forthcoming blur handler (probably dismissing a dialog or popup of some kind) is cancelled. If however the timer expires without such an event, the originally queued blur handler is executed. In the following trace, the default expiration timer is set to 150ms:

*01) LOG: 19:17:01.250:  received focusin on 
DIV#secondary-nav-menu.csc-tabs-container
*02) LOG: 19:17:02.812:  received focusin on 
INPUT#.csc-acquisition-numberPatternChooser-reference-number
*03) LOG: 19:17:04.109:  Cancelled by focus on 
INPUT.csc-numberPatternChooser-button
*04) LOG: 19:17:04.109:  received focusin on 
INPUT.csc-numberPatternChooser-button
*05) LOG: 19:17:05.250:  focusing list
*06) LOG: 19:17:05.250:  received focusin on TABLE.csc-numberPatternChooser-list
*07) LOG: 19:17:05.250:  Cancelled by focus on 
INPUT.csc-numberPatternChooser-button
*08) LOG: 19:17:05.265:  Beginning timer for blur on 
INPUT.csc-numberPatternChooser-button
*09) LOG: 19:17:05.281:  Container focus handler executing
*10) LOG: 19:17:05.281:  focusing item TR.csc-numberPatternChooser-patternRow
*11) LOG: 19:17:05.422:  Timer expired, blurring <=== POPUP CLOSED HERE
*12) LOG: 19:17:05.422:  received focusin on DIV.csc-numberPatternChooser 
cs-numberPatternChooser
*13) LOG: 19:17:05.422:  Beginning timer for blur on 
TR.csc-numberPatternChooser-patternRow cs-selecting
*14) LOG: 19:17:05.578:  Timer expired, blurring

*01), *02) General startup, mouse click on the entry field
*03) TAB key to tbutton
*05) SPACE activates button - list is focused

*07), *08) Anomalous *REFOCUS* and blur of button by IE event handling machinery which cannot really be explained, but *should* be essentially harmless, if it were not for:

*09), *10), *11) Container focus handler executes for selectable row elements - which *SHOULD* have resulted in a focusin event after *10) resulting in cancellation of the blur handler. Instead it simply expires, closing the dialog.

*12), *13), *14) After the disaster of inappropriately closing the popup, various other inappropriate events are generated by IE8, including the processing of an "out-of-order" focus followed by blur.

The core issue appears to be the "failure of faithfulness" in IE8 of the jQuery focus() call executed on the pattern row element. One would expect this either synchronously or within a short period of time to give rise to an event which would be caught by jQuery's own "focusin" handler but it does not. This is an issue encountered in various test cases in Fluid but was until now dealt with in an "ad hoc" manner to make the tests pass correctly under IE8. This is presumably a jQuery framework bug, but in the meantime it appears that bypassing jQuery for programmatic focus and blur calls does indeed give rise to the expected "cancellation event" required by dead man's blur:

*01) LOG: 19:51:26.750:  received focusin on DIV.csc-numberPatternChooser 
cs-numberPatternChooser
*02) LOG: 19:51:27.828:  received focusin on 
INPUT#.csc-acquisition-numberPatternChooser-reference-number
*03) LOG: 19:51:29.297:  Cancelled by focus on 
INPUT.csc-numberPatternChooser-button
*04) LOG: 19:51:29.297:  received focusin on 
INPUT.csc-numberPatternChooser-button
*05) LOG: 19:51:30.640:  focusing list
*06) LOG: 19:51:30.640:  received focusin on TABLE.csc-numberPatternChooser-list
*07) LOG: 19:51:30.640:  Cancelled by focus on 
INPUT.csc-numberPatternChooser-button
*08) LOG: 19:51:30.656:  Beginning timer for blur on 
INPUT.csc-numberPatternChooser-button
*09) LOG: 19:51:30.656:  Container focus handler executing
*10) LOG: 19:51:30.656:  focusing item TR.csc-numberPatternChooser-patternRow
*11) LOG: 19:51:30.656:  Cancelled by focus on 
TR.csc-numberPatternChooser-patternRow (SUCCESSFUL FOCUSIN)
*12) LOG: 19:51:30.656:  received focusin on 
TR.csc-numberPatternChooser-patternRow
*13) LOG: 19:51:30.812:  Timer expired, blurring
<==== DOWN ARROW PRESSED HERE
*14) LOG: 19:51:35.562:  Invoking blur on 
TR.csc-numberPatternChooser-patternRow cs-selecting
*15) LOG: 19:51:35.562:  received focusin on BODY#acquisition
*16) LOG: 19:51:35.562:  Invoking focus on 
TR.csc-numberPatternChooser-patternRow
*17) LOG: 19:51:35.562:  Cancelled by focus on 
TR.csc-numberPatternChooser-patternRow
*18) LOG: 19:51:35.562:  received focusin on 
TR.csc-numberPatternChooser-patternRow
*19) LOG: 19:51:35.578:  Beginning timer for blur on 
TR.csc-numberPatternChooser-patternRow
*20) LOG: 19:51:35.734:  Timer expired, blurring
*21) LOG: 19:51:35.734:  received focusin on DIV.csc-numberPatternChooser 
cs-numberPatternChooser
*22) LOG: 19:51:35.734:  Beginning timer for blur on 
TR.csc-numberPatternChooser-patternRow cs-selecting
*23) LOG: 19:51:35.890:  Timer expired, blurring

Unfortunately, to add insult to injury, this trace reveals a FURTHER problem. Starting the further train of events after *14), the user presses the down arrow to change the selection of the number pattern. This schedules a "blur" train which as seen in our first trace causes a synchronous blur and then focus on the further element. Unfortunately, as *18), *19) reveal, the ORIGINAL blur event continues to propagate asynchronously, not setting off the timer in deadMan'sBlur until *AFTER* the focus event which might cancel it is received.

Dealing with this requires an awkward "proleptic" extension to dead man's blur. It needs to keep track of timestamps for cancellation events and match them up with blurs occuring in time windows BOTH BEFORE AND AFTER the blur handler starts to execute.

Implementing this extension allows NumberPatternChooser to work....

However, for Autocomplete, there is a yet further insult:

LOG: 23:59:02.844:  Applying raw focus to 
LI#matchItem:.cs-autocomplete-matchItem
is followed on expiry of the timer by
LOG: 23:59:02.969:  New value
In this case we have met our match.... even raw triggering of the native "focus" event on IE fails to catch the global "focusin" handler. The only way out is to abandon direct manipulation of "focus" events entirely and declare our own totally fresh events "fluid-focus" and "fluid-blur" which we ensure to fire IN ADDITION to the standard jQuery events, but which are guaranteed to propagate up the DOM tree reliably by virtue of the standard jQuery event mechanism. We then listen to these events in addition to the DOM standard ones.




IN SUMMARY, we identify 3 bugs in the IE8/jQuery system:

BUG 1: IE8 will fire a synchronous FOCUS event (momentarily targetted at the docuemtn) on being given a synthetic blur event BUG 2: IE8 will not notify HANDLERS to the blur event synchronously, but wait until the FOCUS event handler stack returns, this allowing observers to see "out-of-order" service of focus and blur events. BUG 3: Causing synthetic focus with jQuery's "focus" call is not "faithful" on IE and will not give rise to any kind of focus handler notification at all (looks like neither focus nor focusin). Only a call to the raw DOM node's "focus" method will achieve this (and in practice, this has also now been found unreliable).

Fixing bug 3 requires patching the keyboard-a11y plugin to apply raw DOM methods (now - new synthetic focus event via fluid.focus()) for synthetic focus wherever it can.

The presence of bug 1 implies that "globalDismissal" cannot be used since it is 
insufficiently context aware.

Fixing bug 2 requires creation of a "proleptic dead man's blur" which will allow blur handlers to be preemptively cancelled by focus events registered in a short time window BEFORE the blur handler itself starts to execute.



These fixes are now attached to the base FLUID JIRA
http://issues.fluidproject.org/browse/FLUID-3487

The patch version "i" was applied to trunk yesterday and will be part of the Infusion 1.3 final release. This appeared stable and passed many test cases, but turned out not to be sufficient to resolve CSPACE-3304.
Patch version "j" is required to support the behaviour described above on IE8.

The fixes on the CollectionSpace side are attached to the original http://issues.collectionspace.org/browse/CSPACE-3304 as "CSPACE-3304-proleptic-DMB.patch"

These patches are in a "nearly done" state but still include a lot of logging statements to aid debugging on other machines and platforms. Hopefully during the course of the day they will be checked and verified by others.
_______________________________________________________
fluid-work mailing list - [email protected]
To unsubscribe, change settings or access archives,
see http://fluidproject.org/mailman/listinfo/fluid-work

Reply via email to