Explaining the Firefox Warning: Unresponsive script for Silverlight 1.0 apps..

I posted before on the "firefox unresponsive script warning" but enough people have asked off-line to warrant this attempt to explain the issue (since it is not what people expect, and can be tricky to track).

The explanation:
The warning is prompted by Mozilla's heuristics to check on long running scripts (details on these heuristics way below).  The problem is that for silverlight or any plug-in that makes Javascript calls, Firefox some times does not reset the timer for the scripts called from plug-ins: If you fire multiple events from a plug-in the events are obviously handled sequentially, if a new event happens while an event handler is executing, then Firefox does not reset its counter for the script time out; it measures the time from these two events as a single script. 

What does this mean (by example)? :

  • If you run a script that is 11 seconds long, you will get the warning -- this one I understand and expect, this is what Firefox designed for; I do agree it is a good safety measure.
  • If you run 500 event handlers that take 200 milliseconds(each) to run, but are fired continuously and each new event is fired before the previous event handler has completed, then Firefox looks at it as being one very long script and fires their warning...
    • An explicit example:   If you are handling MouseMove in silverlight for an app that is pretty busy, you will likely run into this warning because Firefox will count most of your events as one long script...
  • You can imagine combinations from there... ( for example: 20 scripts that take 0.5 second but are fired continuously, can trigger the warning).. it is this type of random combination that makes the "Warning: Unresponsive script.. " hard to track since most people look for long running scripts and that is not often the case..

How to get around it?
So far window.setTimeout  () seems to work best (though I can't say it works 100% of the time). Your mileage may vary, but here is what I do:

  • For my long running event handlers ( > 2 seconds) , I just call window.setTimeout ( <longrunningCallback>, 0) -- this unfortunately means in some cases the events are slightly delayed as setTimeout will get queued (if there are events ahead this means I might handle them out of order). In most of my scenarios this has been acceptable for long running events; if it is not for you, you could write a pseudo dispatcher that queues all other events so things are always in order but of course you have higher overhead).
  • For short, continuous event handlers (e.g. MouseMove) I don't do setTimeout for all events fired; that seemed like too much overhead..   What I do is:
    • First make sure I narrow the scope of the MouseMove event as much as possible ( so no MouseMove on root element, but as low as I can on the pole).. this way less events get fired
    • then keep a timer and do setTimeout every few seconds ( on a MouseMove, doing it every move can be 10s more times than doing it every two seconds)

Overall, setTimeout is not a perfect workaround, but best I come up with so far.

---

Does the explanation above make sense???  If not, here is a practical repro of the problem, it might make it easier for people to understand..

Requirements:

  • To see the problem you must be running Firefox .. for this repro if you try IE you will see a different problem, since the heuristics for rogue scripts are very differing in IE.. (IE counts statements and my repro loops mindlessly, so creates too many instructions).
  • Also, my repro below assumes you have the default timeouts in Firefox ( 10 seconds).. If yours is longer then tweak timeouts as needed.  See details below on how to find out if your timeouts were tweaked.

Steps to repro:

  1. Open repro page in Firefox.
  2. Click twice quickly on the button that says "run from HTML event" ..  This will run two seven seconds loops from HTML... Technically this is 14 secs of continuous scripts, but since each script is < 10 seconds, firefox works fine..
  3. Click on "Clear status"  HTML button  [optional but helps keep repro readable] ..
  4. Now click twice quickly (same than you did before) in the "run from Silverlight event" button in silverlight..   This event handler is the exact same than we used in Step2..  but you will see that half way through second handler, the "Unresponsive script" warning will get triggered.. Why?? because Firefox considered the two user clicks as one  :(
  5. Now that you have seen the error, check the "use setTimeout workaround" that is on the "Repro via long script" table..
  6. Repeat step 4. My hope is you don't see the error this time around..

To reproduce the problem using a short MouseMove handler ,

  1. Open repro page in firefox
  2. check the "Listen to MouseMove event"
  3. Move the mouse inside the silverlight window continuously (and vigorously) for 13ish seconds..  You should see the warning... If not, continue moving mouse feverishly for long periods of time ..   [if you just can't repro it, try increasing the "handler duration" from 500 milliseconds to 1000]
  4. Once you have seen the error in 3 ...  Check the "useSetTimeout on MouseMove" checkbox;
  5. Repeat the moving of the mouse in Step 3.. My hope is that you don't see the error any more...

 

A few more details or references on Firefox and their timeout heuristics .

Firefox controls its time out based on the dom.max_script_run_time setting, which you can tweak via the about://config moniker; the default timeout in most recent versions is 10 seconds.

  • The setting can not be controlled programmatically (so your script can't tweak it), nor per site ( meaning Firefox chooses to 'trust' your site)..  it is all or nothing, which seems a high ask to the visitors on most sites..
  •  FYI, the issue has been reported to Mozilla and they seem to be on it, but of course given ship cycles, priorities, etc.. we might not see a change for a while.  { I do thank the Mozilla folks as they were quite responsive to confirm that I was not crazy when I was seeing the timeouts on short running scripts)