Friday, September 5, 2008

Launching Browser from External Application

I have a standalone java/swing application, from which I want to launch a browser. There are various methods by which you can do so, but every time this java app calls Runtime.exec to launch, it creates a new browser window (unless the user has set browser such that for new urls, it opens in same window/tab).

My objective is to launch the browser such that, if I want to target a specific browser window, it does that, or if I want to open a new window, it does that too.

And, it should work with both IE and Firefox. Let's not talk about the Google's baby yet.

Now, to target a specific browser window, javascript's window.open is the best choice. However, I can't execute javascript from my java app, I need to have it executed in the browser. What I can do is -

1. Launch the browser from java app, to go to a intermediate URL (servlet/jsp), passing in the window name and the real target URL. This servlet/jsp will inject some javascript code in it to execute on load.
2. This javascript will call window.open function, passing in the target URL and the window name.
3. The target URL will be opened in the new window, or if a window with that name already exists, it will load that target URL page in that window.
4. Use the same javascript code to close this particular window which was originally launched by the java app.

This way, we have the target URL loaded into a named window.

The javascript code snippet is:

var winA=window.open(targetUrl, winName);
window.opener='x'; // this is for IE only
if(winA != window) window.close();

And this works perfectly in IE. However, I have various problems with firefox 2/3.

Allow Javascript to Close Windows in Firefox

Starting from firefox 2, it is not allowed to close a browser window, if that window was not opened by a script. In our case, since the original window was opened by java application, window.close() gives us the error - scripts can not close window not opened by script. The easy workaround for this is to change the firefox config, however, it depends on your specific solution, if you can ask your users to make this config change. I haven't explored the possibility of programatically checking/changing/prompting the users for this change.

Anyway, the change is this - type about:config in firefox address bar. This will give you a bunch of config options. Filter on dom.allow. You should see a config:
dom.allow_scripts_to_close_windows
The default is false, toggle it to true.

Now, you can close browser windows/tabs in firefox using javascript, even if the window/tab was not opened by javascript.

The Same Origin Policy

So far so good. The next problem I have is - getting handle of an already opened window.

You would think that when you use window.open("http://www.google.com", "googleWin"); it will open a window with the name googleWin. This is only partially true. It will open a window, with the URL www.google.com loaded, however, it will not set the name of the window to "googleWin", unless you executed the javascript to load window somewhere from http://www.google.com/....../.

This is a little known security feature of firefox, which I guess is dated back to Netscape days, called Same Origin Policy. According to Mozilla documentation,

The same origin policy prevents document or script loaded from one origin from getting or setting properties of a document from a different origin. Mozilla considers two pages to have the same origin if the protocol, port (if given), and host are the same for both pages.

There's one exception to this rule, where you can allow domain changes upto some extent, for example from http://store.company.com/... to http://company.com/...

Mozilla documentation is here:

http://www.mozilla.org/projects/security/components/same-origin.html

So, if you host the servlet/jsp which opens named window in the same server/protocol/port as the to be opened pages, you will be able to get handle to windows by name.