The other side of the moon

[philiptellis] /bb|[^b]{2}/
Never stop Grokking


Thursday, January 08, 2015

IE throws an "Invalid Calling Object" Exception for certain iframes

On a site that uses boomerang, I found a particular JavaScript error happen very often:
TypeError: Invalid calling object
This only happens on Internet Explorer, primarily IE 11, but I've seen it on versions as old as 9. I searched through stack overflow for the cause of this error, and while many of the cases sounded like they could be my problem, further investigation showed that my case didn't match any of them. The code in particular that threw the exception was collecting resource timing information for all resources on the page. Part of the algorithm involves drilling into iframes on the page, and this error showed up on one particular iframe. There are a few things to note:
   ("performance" in frame) === true;

   frame.hasOwnProperty("performance") === false;
The latter is not a surprise since hasOwnProperty("performance") is not supported for window objects on IE (I've seen this before when investigating JSLint problems.) There was no problem accessing frame.document, but accessing frame.performance threw an exception.
    frame.performance;    // <-- throws "TypeError: Invalid calling object" with error code -2147418113

    frame["performance"]; // <-- throws "TypeError: Invalid calling object" with error code -2147418113
In fact, frame.<anything except document> would throw the same exception. So I looked at the iframe's document object some more, and found this:
    frame.document.pathname === "/xxx/yyy/123/4323.pdf";
The frame was pointing to a PDF document, and while IE was creating a reference to hold the performance object of this document, it prevented any attempts to access this reference. I tested Chrome and Firefox, and they both create and populate a frame.performance object for PDF documents.

Friday, August 22, 2014

jslint's suggestion will break your site: Unexpected 'in'...

I use jslint to validate my JavaScript before it goes out to production. The tool is somewhat useful, but you really have to spend some time ignoring all the false errors it flags. In some cases you can take its suggestions, while in others you can ignore them with no ill effects.

In this particular case, I came across an error, where, if you follow the suggestions, your site will break.

My code looks like this:

   if (!("performance" in window) || !window.performance) {
      return null;
   }

jslint complains saying:

Unexpected 'in'. Compare with undefined, or use the hasOwnProperty method instead.

This is very bad advice for the following reasons:

  • Comparing with undefined will throw an exception on Firefox 31 if used inside an anonymous iframe.
  • Using hasOwnProperty will cause a false negative on IE 10 because window.hasOwnProperty("performance") is false even though IE supports the performance timing object.

So, the only course of action, is to use in for this case.

Thursday, August 08, 2013

Don't guess at TimeZones in JavaScript

I spent quite some time a couple of months ago working on timezone support for mPulse and thought I should document the insanity, but never quite got around to it. Then there was this post on hacker news about reading a user's timezone in JavaScript and using that to display the right time. That post brought back a flood of horrific memories, prompting me to put my thoughts down.

First, while Trevor's post has a good hack to display a time in the user's current timezone, that hack works in only one case -- displaying the current time to the user in their device's timezone.

If you've worked with timezones and front end development for a while, this is probably the first hack you'll think up.  It turns out that in most cases, this is insufficient.

We'll first look at the problems with this approach, and then look at the requirements for proper timezone support.

Problems

  • The user's device timezone is not always correct. Some users fix their device timezone to their home time even when they're travelling, however the information you need to display may be pertinent for the location where they are right now.
  • On the other hand, the user may have their device set to automatically update timezone, but they actually want to see times in their home time (because, for example, that's when they call home, or have their calendar app configured).
  • The timezone offset (which is what you actually get from JavaScript), only tells you the offset from UTC for "right now". This information is irrelevant if you need to display a time that is not now, because daylight saving rules may come into effect.
  • You cannot use a lookup table for offset to timezone, because there isn't a one-to-one mapping between offset and timezone. It's a many-to-many mapping, and it changes.

Second attempt

A second attempt might be to figure out the timezone name by parsing the JavaScript Date.toString() output. This was my second attempt when writing the strftime function for the YUI Library.

I did this study in 2008, and it turns out that browsers are pretty inconsistent wrt Date.toString() output.

Requirements

Ok, before going into this, read this post on stackoverflow about daylight saving time and timezones.

So, what we need is the ability to do the following:

  1. store any date or range of dates.
  2. display a date in any timezone that makes sense for the user, and/or the event(s) being displayed, and/or the environment.
  3. display date ranges that may cross a timezone boundary.
  4. display a historic date in a historic timezone that may have changed due to political decisions.

The first requirement should be pretty straightforward. We'd like to store dates, and the best way is really a unix timestamp or an ISO8601 date. I prefer the latter because it takes into account leap seconds as well (unix timestamps are leap second agnostic [1],[2]). I also always use Zulu time for an ISO8601 date.

This is not sufficient, however. We also need to store the timezone name of the event. This is so that we can display historic events in the timezone they originally occurred in, even if the definition of that timezone changes. This comes from the Olson Database.

With these two pieces of information (event date/time & event timezone name), we can render the date in several ways... the original event date/time, the event date/time relative to the user's current timezone, etc.

We also need to handle date ranges. This could be something like your spring vacation, that just happened to cross several timezones because you left San Francisco on March 8th, flew to the UK, stayed there until April 7th, and then flew back. Your flight departure from SFO is in Pacific Standard Time and your arrival at LHR is in British Standard Time. Your departure from LHR is in British Daylight Time, and your arrival at SFO is in Pacific Daylight Time.

What's most important is that you display these dates in their specific timezones regardless of where the user actually is.

So should we guess or ask the user what they want?

By all means guess at what the user's timezone might be. Use a combination of GeoIP + JavaScript timezone offset to figure out where they might be (note that both of these could be wrong), but give them the option to specify the timezone that they care about.

Also, when displaying event dates, use a date local to the event, but use JavaScript to allow the user an easy way to flip it to their local timezone if they like. That's progressive enhancement.

What else shouldn't we do?

Don't try and guess the user's language or preferred currency from their current location. Always ask and store their response.

Click to help me test boomerang (opens google.com in a new window).

...===...