views:

620

answers:

4

Problem

I'm trying to make a method that passes objects to a similar method in a popup window. I don't have control over the code in the target method, or the object passed in. The target method currently serialises the object, using JSON.stringify where possible, or instanceof Array.

The first problem with this is a bug in IE8 (see below). The second, and more fundamental, is that primitives are not the same across windows:

w = open("http://google.com")
w.Array == Array               // returns false

Overriding on the popup any classes that might be passed in, and then restoring them after the call works, but it's really brittle and a maintenance headache.

Serialising the object into JSON and then parsing it in the context of the window hits the Firefox bug below.

I'm also a bit loathe to do a deep copy of the object or parse the JSON using new w.Object, etc. because it doesn't feel like it should be that complicated.

Can anyone suggest a sensible way to deal with this, or should I just accept that objects can't be passed verbatim between windows?


IE bug

JSON.stringify doesn't work across windows in IE8. If I pass an object to the popup, which attempts to serialise it, stringify returns undefined. To see this problem, open the script console in IE8 and try:

w = open("http://google.com")
JSON.stringify(Object())          // returns "{}"
w.JSON.stringify(w.Object())      // returns "{}"

w.JSON.stringify(Object())        // returns "undefined" on IE8
JSON.stringify(w.Object())        // returns "undefined" on IE8
JSON.stringify([1, w.Object()])   // returns "[1,null]" on IE8

I tried working around this by setting w.JSON = JSON, but as the last test shows, that breaks when you have objects from both windows.

Firefox bug

It seems that calling w.Object() to create an object in Firefox in fact calls window.Object(). The same bug is hit when calling w.JSON.parse or w.eval. To see this, open Firebug's console and try:

w = open("http://google.com")
new w.Object instanceof w.Object        // returns true

w.Object() instanceof w.Object          // returns false on Firefox 3.5
w.Object() instanceof Object            // returns true on Firefox 3.5
w.Object.call(w) instanceof Object      // returns true on Firefox 3.5
w.JSON.parse("{}") instanceof w.Object  // returns false on Firefox 3.5
w.JSON.parse("{}") instanceof Object    // returns true on Firefox 3.5
w.eval("[]") instanceof w.Array         // returns false on Firefox 3.5
w.eval("[]") instanceof Array           // returns true on Firefox 3.5
w.eval.call(w, "[]") instanceof Array   // returns true on Firefox 3.5

The only workaround I can see is parsing the JSON string myself.

A: 

To see this, open Firebug's console and try:

Error: Permission denied for <stackoverflow> to get property Window.Object from <google>. On any line except first one: w = open("http://google.com") Firefox 3.5.7

Think for a moment: You ar trying to open new window with arbitrary site and send to it data available to js. Seems too insecure to allow this.

oh right, open the console on the Google homepage
Mark
+1  A: 

For what it's worth, this is what I'm doing for now:

  • Ensure jquery-json is loaded in the popup window
  • Stringify the object
  • Call w.$.evalJSON(str), which binds primitives correctly
  • Pass that result to the method on the popup

Alternatively (if jquery-json is not available), you could inject the following script into the target:

<script type="text/javascript">
function parseJSON(j) {
  return JSON.parse(j)
}
</script>

as that will capture the popup's JSON, and not the caller's.

Any better solutions gladly appreciated.

Mark
+1  A: 

If you are trying to do cross-domain scripting, seems like JSONP might be worth investigating.

justkt
Good point, I didn't want the extra roundtrip, but this would indeed be necessary if cross domain.
Mark
+1  A: 

I can't say I understand your problem fully, but there's an interesting window.name hack that's worth checking out: http://www.sitepen.com/blog/2008/07/22/windowname-transport/ (the blog post uses dojo but, of course, it can be done with pure JS too). It's safer than JSONP, easy to implement and it seems to work on all browsers.
Basically, it allows you to store any data, of any length in the window.name variable. What's awesome is that this variable doesn't get flushed/cleared on page change/refresh, so with some clever use of iframes you get simple and secure cross-domain transport :)

Igor Klimer
Nice, that's a really handy way to deal with refreshes. The only problem is that I still need to trigger the update in the window, and I don't really want to reload the page.
Mark