The state of concurrent browser windows

Users are used to having multiple browser windows or tabs open concurrently. There is still not an easy way to determine the state of each window.

When all state is embedded within a page, or within the url, the window identity does not need to be tracked at all as each request includes all the necessary state. However there are a couple of significant problems with embedding state into a page, including:

  • limited size of urls
  • the long lifetime of urls (they can be bookmarked and later reused)
  • state encoded into forms gets lost on GET requests (ie link anchors)
  • network bandwidth needed to stream state from server to client
  • not all state may be easily serializable (eg persistence contexts).

Unfortunately, the http protocol has not kept up with these new changes in web usage. The use of a sessionId embedded in either the url or a cookie was invented many years ago to support stateful behaviour per-browser.

There are various ‘solutions’ available to handle per-window state on the server which work at least partially, but none of them are completely satisfactory. The user actions which are particularly problematic are:

  • Right-clicking a link and selecting “open in new window” or “open in new tab”
  • Bookmarking a page, then later opening a new page and activating the bookmark
  • Copying the URL from the browser toolbar and pasting it into a new window

Embedding a window-id in a hidden field

The most reliable way of tracking window identity is to embed a window-id within a hidden field in each form, and mark forms as using POST. Disadvantages using this method:

  • The window identity will be lost as soon as the user does an operation that is not a POST. And this is a critical flaw in most cases; applications that care about window state generally want to allow navigation via links (GET) as well as form posts
  • Losing the window identity value when a GET is done makes it impossible to correctly clean up memory on the server

Embedding a window-id in a cookie

Cookies are pairs that a server can send to a browser, and which the browser will then echo back in each request that it sends to a server. These are provided on all request types (GET, POST, etc). Therefore they are ideal to track window identity, except that all modern browsers treat cookies as shared across all windows in a browser. Setting it in one window affects all other windows too.

There is possibly a way to use cookies to track window identity by the use of javascript. Below is the outline of a possible approach using cookies. The limitations are:

  • It requires javascript (though it is possible to “fall back” to other approaches)
  • It requires session-scoped cookies
  • It requires the server to detect whether the browser supports cookies or not
  • It requires javascript to be rendered into the head of each page

Javascript can be used to update a cookie’s value. Therefore it is possible for links to use an “onclick” handler to set a cookie to contain a window-id, then perform the requested GET operation and immediately reset the cookie. This causes the request to contain the needed window-id while preventing the id from appearing in the page URL (where it can be bookmarked or simply copy-and-pasted).

In addition, the “open in new window” operation will not run the javascript so requests fetched into a new window will not send a window-id and the server can detect this.

Some example code that would be rendered into the head of each page:

window.document.cookie="windowId=-1";
function docookie(link) {
  window.document.cookie="windowId=3";
  window.location.replace(link.href)
  window.document.cookie="windowId=-1";
  return false;
}

Javascript links then look like <a href=”someurl” onclick=”docookie(this)”>..</a>

Embedding a window-id in the URL

The window identity can be embedded within the url as a query parameter.  This means that each form’s action url must contain the magic parameter, and so must the href of every link that is intended to be clicked to perform navigation in the same frame.  A request that does not contain the magic query parameter is regarded as a “new window”, and is allocated a new id. Users must then be prevented from using “open in new window” on links containing the id, as that would copy the magic window-id parameter and both windows would appear to the server to be the same window.  This can be done by setting the “href” parameter of these links to “javascript:0”, and adding an onclick attribute that actually does the navigation by assigning the desired url to property “window.location”.

The FireFox browser simply does not render the “open in new window” option when the href is a javascript link; Internet Explorer does render the menu option but the newly opened window is always blank. Users will want to open new windows, however. Therefore the webapp developer can arrange for some of the links to explicitly use javascript to open a new window and assign it a URL that does not have the magic window-id parameter, therefore causing the request from that window to be assigned a new id. Unfortunately this approach does have some limitations:

  • The window-id value appears in the navigation bar, which is slightly ugly
  • The window-id is saved when a bookmark is made; A simple copy-and-paste of the url also duplicates the window-id parameter
  • Javascript is required; when javascript is not enabled then the whole application becomes unusable as the links are unusable

Because of the above limitations this approach does allow the user to have multiple windows on the same app, each with independent state, but does require them to avoid the problematic behvaiours.

Post-render detection of Window Properties

When a new window is opened, a new javascript Window object is created for it. Javascript can therefore check for a property on the window and if it is not set then it can assume this is a new window.
The major limitations this approach are:

  • Javascript is required
  • Testing can only be done after a page has been rendered. When the test fails (ie this is a new window) then the javascript can discard the current page and request a new one from the server, this time telling the server to allocate a new window-id. However if rendering of the page causes side-effects on the server then these cannot be undone. In most cases this is not an issue as new pages are always populated using GET requests and these should not have side-effects.

The server of course also needs to be told about the id, so this would need to be combined with something like the “Embedding a window-id in the URL” approach. However it does work around some of the limitations of that approach by detecting when a “forbidden” user operation has occurred.


Leave a Reply