Closing Top-Level Windows

Is it possible to close the top-level windows on the server-side? Window#close doesn’t seem to do this.

Context: when a user logs off, I invalidate the session. I want to get rid of any (other) windows/tabs the user may have opened, in order to prevent an “Session Expired/Invalid Security Key” message the next time they try to use the window.

If this is not possible, how should we try and tackle this?

Cheers,

Charles.

Not sure if there is an easier way, but at least the following can be used to close a window:

window.executeJavaScript("close();");

But you can use this
only
for windows opened from the application (that is, with open()), mainly pop-up windows, not for browser windows opened by the user. It’s a browser security thing I guess.

The above line closes the
current
window for which you are processing an event, so the other windows need to close themselves after polling the server or something. Or, perhaps you could access the other windows from JavaScript to close them.

Ah. That’s precisely where I want to use it! At least that explains why it didn’t work (I’d tried this one already), along with the only-works-on-the-current-window issue, which I know about but hadn’t directly thought about.

Anyone else have any thoughts on this?

Cheers,

Charles.

The security model is such that id a window (P) have opened another window (C), only P and C can call C.close();

In practice there are two approaches:

  1. store the window reference on javascript side and use it for calling close() afterwards. Problem is that if you reload the page, you loose the reference and you can not close window anymore.

  2. add polling/push to window to be closed

A really messy example of the latter approach below

package com.example.wt;

import com.vaadin.Application;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.*;
import com.vaadin.ui.Button.ClickEvent;

public class WtApplication extends Application {
	Window c;
	public void init() {
		final Window mainWindow = new Window("Wt Application");
		mainWindow.addComponent(new Button("Open a browser level window",
				new Button.ClickListener() {

					public void buttonClick(ClickEvent event) {
						c = new Window(
								"Another window opened by the main window");
						c.addComponent(new Button("Close this window",
								new Button.ClickListener() {
									public void buttonClick(ClickEvent event) {
										c.executeJavaScript("close()");
									}
								}));
						WtApplication.this.addWindow(c);
						mainWindow.open(new ExternalResource(c.getURL()), c.getName());
						c.addComponent(new ProgressIndicator());
					}
				}));
		mainWindow.addComponent(new Button("Close external window", new Button.ClickListener() {
			
			public void buttonClick(ClickEvent event) {
				c.executeJavaScript("close()");				
			}
		}));
		
		setMainWindow(mainWindow);
	}

}

And here is an example of the first approach. Quite a hack :slight_smile:

package com.example.wt;

import com.vaadin.Application;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.*;
import com.vaadin.ui.Button.ClickEvent;

public class WtApplication extends Application {
	Window c;
	public void init() {
		final Window mainWindow = new Window("Wt Application");
		mainWindow.addComponent(new Button("Open a browser level window",
				new Button.ClickListener() {

					public void buttonClick(ClickEvent event) {
						c = new Window(
								"Another window opened by the main window");
						c.addComponent(new Button("Close this window",
								new Button.ClickListener() {
									public void buttonClick(ClickEvent event) {
										c.executeJavaScript("close()");
									}
								}));
						WtApplication.this.addWindow(c);
						mainWindow.executeJavaScript("w_" + c.getName() + " = open('" + c.getURL() + "', '_new')");
					}
				}));
		mainWindow.addComponent(new Button("Close external window", new Button.ClickListener() {
			
			public void buttonClick(ClickEvent event) {
				mainWindow.executeJavaScript("w_" + c.getName() + ".close()");				
			}
		}));
		
		setMainWindow(mainWindow);
	}
}

Oh… Forgot the real recommendation:

Do not use browser windows - they will cause headaches even it they work perfectly. Some of the users will always have a popup blocker on and just wonders why the application is “broken”.

Thanks Joonas.

The intention is not that we (as in the application) create new windows, but that the user may choose themselves to create new windows (right click on link, open in new window/tab).

We could, of course, simulate this ourselves with our own tabs but
a) that seems redundant
b) takes up screen valuable real-estate[1]

c) Means we need to double up every “open view” button with “open view in new tab”

I guess we need to experiment.

Cheers

Charles.

[1]
We need to target a base monitor resolution of 1024x768, since there are still a large number of users out there in corporate land with this configuration.

If you looking at the default configuration of IE8 and Windows (covering XP, Vista and Windows 7) - again huge use out there in our market - and take away the screen estate used by the OS taskbar, the “chrome” of the browser - title bar, toolbar, address/back/forward buttons, tabs, status bar at the bottom - that gives us a viewport space of just 990x546px for our application : as you can see vertical Screenspace is rather a precious commodity!

In that case, the only working solution would be “2) add polling/push to window to be closed”.

I suppose a basic solution would be:

  1. If there are more than one open window, keep the session alive if the user logs out
  2. Have polling or push in all windows to check changes in the logged-in state
  3. All windows should just reset their UI and display “You have logged out” or switch to a login view instead of closing

Thanks All.

I’ve gone for a middle-ground solution for now : when I said in the first post “Context: when a user logs off, I invalidate the session”, what I actually meant is “Context: we are using
Apache Shiro
; when a user is logged off, Shiro invalidates the session”

I have changed my Shiro configuration to not invalidate the session when the user logs off [1]
This prevents the nasty Session Expired/Invalid Security Key message.

At some point, when we’ve filled all the bits in, we will probably use server-push to redirect the windows. I tried doing this this morning, but didn’t get very far! No matter.

BTW : I recommend using Apache Shiro for security purposes. It’s simple, non-invasive and feature-rich. I know that Les Hazlewood, the project lead, uses (or has used Vaadin) too!

Thanks again,

Cheers,

Charles

[1]
by creating and using a subclass of DefaultWebSecuirytManager and overriding #stopSession(Subject) with a no-op.

As a bit related login-logout issue, I investigated an old question about logging in from an external HTML page and came up with
this solution
.

Just for note.

I have just had to deal with the same problem. Our solution was the following:

[1]
disabled session expiry notification (using
CustomizedSystemMessages
)

[2]
set session expired URL to point to an html page that closes itself on loading.

[3]
added a
Refresher
component to each window in my app (to enable polling)

The effect is that when the session expires all open browser windows close within a second or two.