UI.reinit() is now called when an existing, preserved UI is shown after a browser reload of the current page. The default implementation is empty. The browser window size and location are up to date in UI.reinit(); window resize and URI fragment listeners, if any, will be called after returning from UI.reinit(). Change-Id: Ie7aa670aaecf8e0e1510c91325b2a137b41263aftags/7.2.0.beta1
@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
import com.vaadin.server.UIProvider; | |||
import com.vaadin.ui.UI; | |||
/** | |||
* Marks a UI that should be retained when the user refreshed the browser | |||
@@ -30,6 +31,10 @@ import com.vaadin.server.UIProvider; | |||
* adding this annotation to a UI class, the framework will instead reuse the | |||
* current UI instance when a reload is detected. | |||
* <p> | |||
* Whenever a request is received that reloads a preserved UI, the UI's | |||
* {@link UI#reinit(com.vaadin.server.VaadinRequest) reinit} method is invoked | |||
* by the framework. | |||
* <p> | |||
* By using | |||
* {@link UIProvider#isPreservedOnRefresh(com.vaadin.server.UICreateEvent)}, the | |||
* decision can also be made dynamically based on other parameters than only |
@@ -32,6 +32,7 @@ import com.vaadin.shared.ui.ui.PageClientRpc; | |||
import com.vaadin.shared.ui.ui.PageState; | |||
import com.vaadin.shared.ui.ui.UIConstants; | |||
import com.vaadin.shared.ui.ui.UIState; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.ui.JavaScript; | |||
import com.vaadin.ui.LegacyWindow; | |||
import com.vaadin.ui.Link; | |||
@@ -635,6 +636,9 @@ public class Page implements Serializable { | |||
} | |||
public void init(VaadinRequest request) { | |||
// NOTE: UI.reinit makes assumptions about the semantics of this method. | |||
// It should be kept in sync if this method is changed. | |||
// Extract special parameter sent by vaadinBootstrap.js | |||
String location = request.getParameter("v-loc"); | |||
String clientWidth = request.getParameter("v-cw"); | |||
@@ -676,28 +680,51 @@ public class Page implements Serializable { | |||
} | |||
/** | |||
* Updates the internal state with the given values. Does not resize the | |||
* Page or browser window. | |||
* | |||
* For internal use only. Updates the internal state with the given values. | |||
* Does not resize the Page or browser window. | |||
* | |||
* @deprecated As of 7.2, use | |||
* {@link #updateBrowserWindowSize(int, int, boolean)} instead. | |||
* | |||
* @param width | |||
* The new width | |||
* the new browser window width | |||
* @param height | |||
* The new height | |||
* the new browse window height | |||
*/ | |||
@Deprecated | |||
public void updateBrowserWindowSize(int width, int height) { | |||
boolean fireEvent = false; | |||
updateBrowserWindowSize(width, height, true); | |||
} | |||
/** | |||
* For internal use only. Updates the internal state with the given values. | |||
* Does not resize the Page or browser window. | |||
* | |||
* @since 7.2 | |||
* | |||
* @param width | |||
* the new browser window width | |||
* @param height | |||
* the new browser window height | |||
* @param fireEvents | |||
* whether to fire {@link BrowserWindowResizeEvent} if the size | |||
* changes | |||
*/ | |||
public void updateBrowserWindowSize(int width, int height, | |||
boolean fireEvents) { | |||
boolean sizeChanged = false; | |||
if (width != browserWindowWidth) { | |||
browserWindowWidth = width; | |||
fireEvent = true; | |||
sizeChanged = true; | |||
} | |||
if (height != browserWindowHeight) { | |||
browserWindowHeight = height; | |||
fireEvent = true; | |||
sizeChanged = true; | |||
} | |||
if (fireEvent) { | |||
if (fireEvents && sizeChanged) { | |||
fireEvent(new BrowserWindowResizeEvent(this, browserWindowWidth, | |||
browserWindowHeight)); | |||
} | |||
@@ -917,14 +944,37 @@ public class Page implements Serializable { | |||
/** | |||
* For internal use only. Used to update the server-side location when the | |||
* client-side location changes. | |||
* | |||
* @deprecated As of 7.2, use {@link #updateLocation(String, boolean)} | |||
* instead. | |||
* | |||
* @param location | |||
* the new location URI | |||
*/ | |||
@Deprecated | |||
public void updateLocation(String location) { | |||
updateLocation(location, true); | |||
} | |||
/** | |||
* For internal use only. Used to update the server-side location when the | |||
* client-side location changes. | |||
* | |||
* @since 7.2 | |||
* | |||
* @param location | |||
* the new location URI | |||
* @param fireEvents | |||
* whether to fire {@link UriFragmentChangedEvent} if the URI | |||
* fragment changes | |||
*/ | |||
public void updateLocation(String location, boolean fireEvents) { | |||
try { | |||
String oldUriFragment = this.location.getFragment(); | |||
this.location = new URI(location); | |||
String newUriFragment = this.location.getFragment(); | |||
if (newUriFragment == null ? oldUriFragment != null | |||
: !newUriFragment.equals(oldUriFragment)) { | |||
if (fireEvents | |||
&& !SharedUtil.equals(oldUriFragment, newUriFragment)) { | |||
fireEvent(new UriFragmentChangedEvent(this, newUriFragment)); | |||
} | |||
} catch (URISyntaxException e) { |
@@ -129,6 +129,11 @@ public abstract class UIProvider implements Serializable { | |||
* detect that the application is opened in a browser window where it has | |||
* previously been open. The framework attempts to discover this by checking | |||
* the value of window.name in the browser. | |||
* <p> | |||
* Whenever a preserved UI is reused, its | |||
* {@link UI#reinit(com.vaadin.server.VaadinRequest) reinit} method is | |||
* invoked by the framework first. | |||
* | |||
* | |||
* @param event | |||
* the UI create event with information about the UI and the |
@@ -266,12 +266,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler { | |||
*/ | |||
private void reinitUI(UI ui, VaadinRequest request) { | |||
UI.setCurrent(ui); | |||
// Fire fragment change if the fragment has changed | |||
String location = request.getParameter("v-loc"); | |||
if (location != null) { | |||
ui.getPage().updateLocation(location); | |||
} | |||
ui.doReinit(request); | |||
} | |||
/** |
@@ -16,6 +16,7 @@ | |||
package com.vaadin.ui; | |||
import java.net.URI; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
@@ -27,6 +28,7 @@ import java.util.concurrent.Future; | |||
import java.util.logging.Level; | |||
import java.util.logging.Logger; | |||
import com.vaadin.annotations.PreserveOnRefresh; | |||
import com.vaadin.event.Action; | |||
import com.vaadin.event.Action.Handler; | |||
import com.vaadin.event.ActionManager; | |||
@@ -161,7 +163,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements | |||
public void resize(int viewWidth, int viewHeight, int windowWidth, | |||
int windowHeight) { | |||
// TODO We're not doing anything with the view dimensions | |||
getPage().updateBrowserWindowSize(windowWidth, windowHeight); | |||
getPage().updateBrowserWindowSize(windowWidth, windowHeight, true); | |||
} | |||
@Override | |||
@@ -361,7 +363,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements | |||
if (variables.containsKey(UIConstants.LOCATION_VARIABLE)) { | |||
String location = (String) variables | |||
.get(UIConstants.LOCATION_VARIABLE); | |||
getPage().updateLocation(location); | |||
getPage().updateLocation(location, true); | |||
} | |||
} | |||
@@ -659,6 +661,57 @@ public abstract class UI extends AbstractSingleComponentContainer implements | |||
*/ | |||
protected abstract void init(VaadinRequest request); | |||
/** | |||
* Internal reinitialization method, should not be overridden. | |||
* | |||
* @since 7.2 | |||
* @param request | |||
* the request that caused this UI to be reloaded | |||
*/ | |||
public void doReinit(VaadinRequest request) { | |||
// This is a horrible hack. We want to have the most recent location and | |||
// browser window size available in reinit(), but we want to call | |||
// listeners, if any, only after reinit(). So we momentarily assign the | |||
// old values back before setting the new values again to ensure the | |||
// events are properly fired. | |||
Page page = getPage(); | |||
URI oldLocation = page.getLocation(); | |||
int oldWidth = page.getBrowserWindowWidth(); | |||
int oldHeight = page.getBrowserWindowHeight(); | |||
page.init(request); | |||
reinit(request); | |||
URI newLocation = page.getLocation(); | |||
int newWidth = page.getBrowserWindowWidth(); | |||
int newHeight = page.getBrowserWindowHeight(); | |||
page.updateLocation(oldLocation.toString(), false); | |||
page.updateBrowserWindowSize(oldWidth, oldHeight, false); | |||
page.updateLocation(newLocation.toString(), true); | |||
page.updateBrowserWindowSize(newWidth, newHeight, true); | |||
} | |||
/** | |||
* Reinitializes this UI after a browser refresh if the UI is set to be | |||
* preserved on refresh, typically using the {@link PreserveOnRefresh} | |||
* annotation. This method is intended to be overridden by subclasses if | |||
* needed; the default implementation is empty. | |||
* <p> | |||
* The {@link VaadinRequest} can be used to get information about the | |||
* request that caused this UI to be reloaded. | |||
* | |||
* @since 7.2 | |||
* @param request | |||
* the request that caused this UI to be reloaded | |||
*/ | |||
protected void reinit(VaadinRequest request) { | |||
} | |||
/** | |||
* Sets the thread local for the current UI. This method is used by the | |||
* framework to set the current application whenever a new request is |
@@ -0,0 +1,116 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.ui; | |||
import org.easymock.EasyMock; | |||
import org.easymock.IMocksControl; | |||
import org.junit.Assert; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import com.vaadin.server.Page.BrowserWindowResizeEvent; | |||
import com.vaadin.server.Page.BrowserWindowResizeListener; | |||
import com.vaadin.server.Page.UriFragmentChangedEvent; | |||
import com.vaadin.server.Page.UriFragmentChangedListener; | |||
import com.vaadin.server.VaadinRequest; | |||
public class UIInitReinitTest { | |||
private boolean initCalled; | |||
private boolean reinitCalled; | |||
private boolean fragmentChangeCalled; | |||
private boolean browserWindowResizeCalled; | |||
private class TestUI extends UI implements UriFragmentChangedListener, | |||
BrowserWindowResizeListener { | |||
@Override | |||
protected void init(VaadinRequest request) { | |||
getPage().addBrowserWindowResizeListener(this); | |||
getPage().addUriFragmentChangedListener(this); | |||
initCalled = true; | |||
Assert.assertEquals("foo", getPage().getUriFragment()); | |||
Assert.assertEquals(100, getPage().getBrowserWindowWidth()); | |||
Assert.assertEquals(100, getPage().getBrowserWindowHeight()); | |||
Assert.assertFalse(fragmentChangeCalled); | |||
Assert.assertFalse(browserWindowResizeCalled); | |||
} | |||
@Override | |||
protected void reinit(VaadinRequest request) { | |||
reinitCalled = true; | |||
Assert.assertEquals("bar", getPage().getUriFragment()); | |||
Assert.assertEquals(200, getPage().getBrowserWindowWidth()); | |||
Assert.assertEquals(200, getPage().getBrowserWindowHeight()); | |||
Assert.assertFalse(fragmentChangeCalled); | |||
Assert.assertFalse(browserWindowResizeCalled); | |||
} | |||
@Override | |||
public void browserWindowResized(BrowserWindowResizeEvent event) { | |||
Assert.assertEquals(200, event.getWidth()); | |||
Assert.assertEquals(200, event.getHeight()); | |||
browserWindowResizeCalled = true; | |||
} | |||
@Override | |||
public void uriFragmentChanged(UriFragmentChangedEvent event) { | |||
Assert.assertEquals("bar", event.getUriFragment()); | |||
fragmentChangeCalled = true; | |||
} | |||
}; | |||
@Before | |||
public void setUp() { | |||
initCalled = reinitCalled = fragmentChangeCalled = browserWindowResizeCalled = false; | |||
} | |||
@Test | |||
public void testListenersCalled() { | |||
IMocksControl control = EasyMock.createNiceControl(); | |||
VaadinRequest initRequest = control.createMock(VaadinRequest.class); | |||
EasyMock.expect(initRequest.getParameter("v-loc")).andReturn( | |||
"http://example.com/#foo"); | |||
EasyMock.expect(initRequest.getParameter("v-cw")).andReturn("100"); | |||
EasyMock.expect(initRequest.getParameter("v-ch")).andReturn("100"); | |||
VaadinRequest reinitRequest = control.createMock(VaadinRequest.class); | |||
EasyMock.expect(reinitRequest.getParameter("v-loc")).andReturn( | |||
"http://example.com/#bar"); | |||
EasyMock.expect(reinitRequest.getParameter("v-cw")).andReturn("200"); | |||
EasyMock.expect(reinitRequest.getParameter("v-ch")).andReturn("200"); | |||
control.replay(); | |||
UI ui = new TestUI(); | |||
ui.doInit(initRequest, 0, ""); | |||
Assert.assertTrue(initCalled); | |||
Assert.assertFalse(fragmentChangeCalled); | |||
Assert.assertFalse(browserWindowResizeCalled); | |||
ui.doReinit(reinitRequest); | |||
Assert.assertTrue(reinitCalled); | |||
Assert.assertTrue(fragmentChangeCalled); | |||
Assert.assertTrue(browserWindowResizeCalled); | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.ui; | |||
import com.vaadin.annotations.PreserveOnRefresh; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.ui.Label; | |||
@PreserveOnRefresh | |||
public class UIReinit extends AbstractTestUI { | |||
public static final String REINIT_ID = "reinit"; | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
} | |||
@Override | |||
protected void reinit(VaadinRequest request) { | |||
Label l = new Label("Reinit!"); | |||
l.setId(REINIT_ID); | |||
addComponent(l); | |||
} | |||
@Override | |||
public String getTestDescription() { | |||
return "UI reinit after refresh"; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return Integer.valueOf(12191); | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
/* | |||
* Copyright 2000-2013 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.ui; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
public class UIReinitTest extends MultiBrowserTest { | |||
@Test | |||
public void testUIReinit() { | |||
openTestURL(); | |||
Assert.assertFalse(reinitLabelExists()); | |||
// Reload the page; UI.reinit should be invoked | |||
openTestURL(); | |||
Assert.assertTrue(reinitLabelExists()); | |||
} | |||
private boolean reinitLabelExists() { | |||
return !getDriver().findElements(By.id(UIReinit.REINIT_ID)).isEmpty(); | |||
} | |||
} |