function call from new window not allowed in firefox?

34 views
Skip to first unread message

Largo

unread,
Jan 29, 2007, 5:04:06 AM1/29/07
to Google Web Toolkit
In my application I want to open a new window and in this window I
want to have a link which calls a function from my application. Below
you can find an example of how this would look like.

public class foo implements EntryPoint {

public void onModuleLoad() {
Button button = new Button ("open window");
button.addClickListener(new buttonClicklistener());
RootPanel.get().add(button);
}


private class buttonClicklistener implements ClickListener {

public void onClick(Widget sender) {
openWindow();
}
}

private native void openWindow() /*-{
var window = $wnd.open("", "_blank");
if (window.opener == null) window.opener = self;
window.document.createElement("body");
window.document.body.innerHTML = "<a href=\"javascript:
window.opener.sayHello();\">say hello</a>";
$wnd.sayHello = function () { @com.test.client.foo::sayHello()() };
}-*/;

public static void sayHello() {
Window.alert("hello");
}

}

Now the problem is that this works perfectly in internet explorer, but
not in firefox. There I get an exception: no authorization to read the
property Window.sayHello (translated from dutch). I know this has
something to do with the fact that firefox doesn't allow calls from a
different origin. Isn't there a way to make sure firefox knows this
new window is from the same source?

Dan Morrill

unread,
Jan 29, 2007, 10:15:32 PM1/29/07
to Google-We...@googlegroups.com

Hi, Largo!

You have started down the dark path of JavaScript, and it is fraught with peril. :)

First things first, though -- here is why your current code doesn't work:  When you load a new browser window passing "" as the url, FireFox essentially treats that as "about:blank".  Webkit (Safari) and IE treat a blank URL as in the same domain as the window which spawned the new window, but FireFox treats it as a separate domain.  Thus, you are prevented from accessing it, by the Same-Origin policy.

So, the fix seems logical -- replace your version of the JSNI method with this version:

     var newWin = $wnd.open("blank.html", "_blank");
     newWin.master = $wnd;
     newWin.document.body.innerHTML = "<a href=\"javascript:window.master.sayHello();\">say hello</a>";
     $wnd.sayHello = function () { @com.google.gwt.hypersimple.client.HyperSimple::sayHello()() };

What this version does is load " blank.html", which is an HTML file consisting of:  "<html></html>".  This causes FireFox to load blank.html from the same site where it loaded your GWT host page, thus appeasing the fickle spirits of Same-Origin.

However, if you actually try that code, you'll find that it still doesn't work either, unless you insert an alert immediately before the line where you assign body.innerHTML.  The reason is that you can't assign to innerHTML until the new blank.html page has actually loaded.  If you insert an alert() immediately before the innerHTML assignment, it works because the alert pauses execution of JavaScript long enough for blank.html to load.

In other words:  you can't assume that either window will load before the other.  The opener can't assume that the opened window's HTML will load before it starts inserting innerHTML.  Likewise, the opened window can't assume that the opener has finished setting up, by the time the opened window is ready.

The only safe way to do this that I can think of is to have the opener set up everything that the opened window could possibly need before actually opening it, and then have the opened window use an onload hook to check back with the opener, once it has loaded.

That means that blank.html now becomes:
<html><body onload="window.opener.initChild(window);"></body></html>

And your openWindow() method becomes:


  private native void openWindow() /*-{
     $wnd.initChild = function(theWin) {
       theWin.document.body.innerHTML = "<a href=\"javascript:window.opener.sayHello();\">say hello</a>";
     };
     $wnd.sayHello = @ com.google.gwt.hypersimple.client.HyperSimple::sayHello();
     $wnd.childWin = $wnd.open("blank.html", "_blank");
   }-*/;

This code has the opener create a callback function under a well-known name ("initChild") in its own local namespace.  The blank.html page in turn calls that well-known callback via "window.opener.initChild", passing it its own window handle, which gives the callback a handle to add contents to the child window.  initChild in turn does whatever setup it needs to do, and the child page can call arbitrary functions on the parent page (such as sayHello).

Note that the child window could also do arbitrary JavaScript setup in there too -- it could even be its own GWT app, I think.  It just needs to explicitly call "initChild(window)" when it's done.

Note also that you don't have deal with setting window.opener -- I am unaware of any browser that doesn't set that up correctly in this scenario.

I have tested this version in WebKit (OS X hosted mode) and FireFox (on OS X.)  It appears to work fine in both cases.  Let me know whether and how it works for you!

- Dan Morrill

On 1/29/07, Largo <Larg...@gmail.com> wrote:

In my application I want to open a new window and in this window I
want to have a link which calls a function from my application. Below
you can find an example of how this would look like.

public class foo implements EntryPoint {

        public void onModuleLoad() {
            Button button = new Button ("open window");
            button.addClickListener(new buttonClicklistener());
            RootPanel.get().add(button);
          }


          private class buttonClicklistener implements ClickListener {

               public void onClick(Widget sender) {
                 openWindow();
               }
          }

          private native void openWindow() /*-{
            var window = $wnd.open("", "_blank");
        if (window.opener == null) window.opener = self;
        window.document.createElement ("body");

Largo

unread,
Jan 31, 2007, 5:00:12 AM1/31/07
to Google Web Toolkit
Hi Dan,

Many thanks for your solution. It works perfect both in firefox and
IE. Although I knew more or less the source of the problem, I don't
think I would've found the solution myself.

Meanwhile you also solved something else I didn't understand, because
even with my previous version, I needed an alert after creating the
window to make it work in hosted mode. It seems in hosted mode, even a
blank url needs some time to be created.

Thanks again!
Largo

Reply all
Reply to author
Forward
0 new messages