here we collect ideas for http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-949 btw. http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-11
this site contains:
- possible approaches to detect a window based on different kinds of IDs
- possibilities for closing logical windows (represented by an ID) and opening new logical windows manually
this site doesn't include:
- information about a concrete scope
the result can be used for:
- window distinction
state (including custom-scope) history per window -> basic back-button support
- double submit prevention
FacesContext#getWindowId independent of other apis, new artifacts,... we should provide this method to expose the current window-id easily.
new attributes for existing components
command components should get the following new attribute(s) to control the window-id (e.g. to open a popup with a different window-id). this information has to be sent as request parameter (javax.faces.windowId), because the information needs to be available before restoring the view.
- windowId (default: current values current|new|none)
if no information is provided via javax.faces.windowId, the windowId parameter of the url will be used (if it exists, otherwise a new window-id will be created). we need "none" e.g. for old command components which don't have this attribute. in this case we might need a fallback (depending on what we want to support).
storing the window-id
the usual strategy is to store the window-id in the url due to the different browser restrictions. we could also do it via state-saving but there are also cases which would break the whole mechanism (e.g. a page refresh or pressing enter in the address-space). some of those cases already break the view-scope.
the only problems with the window-id in the url are:
- pretty urls
- opening an url in a new window.
- for 1) window-ids shouldn't be active by default to ensure backward compatibility and with html5 it's possible to rewrite the url on the client-side
for 2) there is a script which detects it - we have to render it (see http://old.nabble.com/-CODI--lazy-windowId-drop-script-p31654496.html)
for 2) the page gets loaded before the window-id can be discarded. that might lead to an unexpected request and therefore an unexpected behaviour. -> we have to think about a request token.
in codi we have automatedEntryPoint to allow that external apps can query pages without creating new windows.
DRAFT: Discussion ....
Known solutions for storing the window-id on the client-side (which doesn't support all use-cases on the client-side):
- Hidden field
- No support for GET requests and manual page refreshes
- Cookie only
- Doesn't work at all
- URL only
- Clones a window with "open in new tab" and similar
URL + js for detecting new windows (see http://old.nabble.com/-CODI--lazy-windowId-drop-script-p31654496.html)
- URL (window-id) + Cookie (request-id) which gets set via js directly before the request + reset of the Cookie asap
- Also supports GET requests -- possible problem (depending on the browser): parallel windows which set the cookies in parallel; additional mouse buttons e.g. binding of the middle mouse button)
- URL + request-id in a hidden field for POST requests and
- sessionStorage of HTML5
- Looks promising with Firefox but not with other browsers
- Client-side window-handler of CODI
A completely working solution with the drawbacks of page-flickering and the incompatibility with libs like PrettyFaces.
Mixed approach of the first prototype
detect if it's a valid request before the FacesContext gets created. a valid request is a request with a window-id AND a request-id (the request id is linked to a window-id and not valid with other window-ids) the request-id is stored in a hidden field and gets transferred to a cookie before a request -> it isn't in the url and it isn't shared between tabs.
no window-id (and maybe an old cookie with request-id) -> invalid window -> create a new window -> render small page which sets the cookie with the new request-id and requests the same url again + a window-id
window-id is in the url -> page-refresh is supported because the first request-id is still in the cookie -> state gets rolled back to this old request-id get or post requests -> a js has to executed to transfer the new request-id from the hidden field to the cookie (only in case of the left mouse-button without other keys and keys like the enter key) (hidden field per view-root (not per form)).
ajax requests have to update the hidden field with the request-id in >any< case.
open a new tab and enter the url or open a bookmark -> it's the same like the first request scenario -> done
restriction: creating a bookmark -> opening a new tab and using this bookmark immediately - that will clone the window because the request-id in the cookie is valid for this window-id
click "open in new tab" via something else than left mouse-button without other keys and keys like the enter key -> remove the cookie (needs to be tested with all browsers) -> new tab gets opened with window-id (from the url) but the cookie was removed -> invalid request -> new window gets created
restriction: in some cases onclick won't get called -> no cleanup -> cloned window -> http://old.nabble.com/-CODI--lazy-windowId-drop-script-p31654496.html is needed
if all that works, the only constellations which don't work are:
switching to the old tab and refreshing it because the request-id of the old tab has been overridden by the request-id of the new tab -> invalid request -> new window)
- creating a bookmark and opening it immediately in a new tab
we still need http://old.nabble.com/-CODI--lazy-windowId-drop-script-p31654496.html -> we have a quite complex approach compared to #4 but we would also get request id's.
Frameworks like Stripes has a Flash Scope that support multiple windows using a query param.
Common use cases
1. Use target="_blank"
<form action="/faces/file.xhtml" method="POST" target="_blank"> ... </form>
2. Use window.open :
<form action="/faces/file.xhtml" method="POST" target="foo" onsubmit="window.open('', 'foo', 'width=450,height=300,status=yes,resizable=yes,scrollbars=yes')"> ... </form>