Skip to content

Commit

Permalink
Support for HTML5 push/replaceState for proper deep linking features (#…
Browse files Browse the repository at this point in the history
…8116)

* Added support for HTML5 push/replaceState for proper deep linkin features

* Automated test script now works at least on chrome

* Uses html5 push/popstate to implement uri fragment feature

* fire legacy fragment change events also via popstate events rpc calls
* send new fragments via pushstate mechanism

* formatting

* Formatting and adding test and workaround for IE bug

* Formatting and depracated UriFragmentListener

* Aligned naming in the new API

* Ignored IE due to web driver bug

Tested a workaround with javascript based window.location.href fetch,
but that don’t seem to work stable enough.
  • Loading branch information
mstahv authored and pleku committed Jan 13, 2017
1 parent fe536b3 commit 62c44dd
Show file tree
Hide file tree
Showing 14 changed files with 447 additions and 109 deletions.
63 changes: 0 additions & 63 deletions client/src/main/java/com/vaadin/client/ui/VUI.java
Expand Up @@ -26,11 +26,7 @@
import com.google.gwt.event.logical.shared.HasResizeHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.SimplePanel;
Expand All @@ -46,7 +42,6 @@
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
import com.vaadin.client.ui.ui.UIConnector;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.ui.ui.UIConstants;

/**
*
Expand Down Expand Up @@ -99,51 +94,8 @@ public class VUI extends SimplePanel implements ResizeHandler,
/** For internal use only. May be removed or replaced in the future. */
public boolean resizeLazy = false;

private HandlerRegistration historyHandlerRegistration;

private TouchScrollHandler touchScrollHandler;

/**
* The current URI fragment, used to avoid sending updates if nothing has
* changed.
* <p>
* For internal use only. May be removed or replaced in the future.
*/
public String currentFragment;

/**
* Listener for URI fragment changes. Notifies the server of the new value
* whenever the value changes.
*/
private final ValueChangeHandler<String> historyChangeHandler = new ValueChangeHandler<String>() {

@Override
public void onValueChange(ValueChangeEvent<String> event) {
String newFragment = event.getValue();

// Send the location to the server if the fragment has changed
// and flush active connectors in UI.
if (!newFragment.equals(currentFragment) && connection != null) {
/*
* Ensure the fragment is properly encoded in all browsers
* (#10769)
*
* createUrlBuilder does not properly pass an empty fragment to
* UrlBuilder on Webkit browsers so do it manually (#11686)
*/
String location = Window.Location.createUrlBuilder()
.setHash(URL
.decodeQueryString(Window.Location.getHash()))
.buildString();

currentFragment = newFragment;
connection.flushActiveConnector();
connection.updateVariable(id, UIConstants.LOCATION_VARIABLE,
location, true);
}
}
};

private VLazyExecutor delayedResizeExecutor = new VLazyExecutor(200,
new ScheduledCommand() {

Expand Down Expand Up @@ -186,21 +138,6 @@ public void run() {
}
}

@Override
protected void onAttach() {
super.onAttach();
historyHandlerRegistration = History
.addValueChangeHandler(historyChangeHandler);
currentFragment = History.getToken();
}

@Override
protected void onDetach() {
super.onDetach();
historyHandlerRegistration.removeHandler();
historyHandlerRegistration = null;
}

/**
* Stop monitoring for parent element resizes.
*/
Expand Down
68 changes: 38 additions & 30 deletions client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java
Expand Up @@ -43,18 +43,17 @@
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.Focusable;
Expand Down Expand Up @@ -105,6 +104,8 @@
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.UI;

import elemental.client.Browser;

@Connect(value = UI.class, loadStyle = LoadStyle.EAGER)
public class UIConnector extends AbstractSingleComponentContainerConnector
implements Paintable, MayScrollChildren {
Expand All @@ -115,6 +116,12 @@ public class UIConnector extends AbstractSingleComponentContainerConnector

private HandlerRegistration windowOrderRegistration;

/*
* Used to workaround IE bug related to popstate events and certain fragment
* only changes
*/
private String currentLocation;

private final StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
Expand Down Expand Up @@ -232,6 +239,28 @@ public void onScroll(ScrollEvent event) {
}
}
});

Browser.getWindow().setOnpopstate(evt -> {
final String newLocation = Browser.getWindow().getLocation()
.toString();
getRpcProxy(UIServerRpc.class).popstate(newLocation);
currentLocation = newLocation;
});
// IE doesn't fire popstate correctly with certain hash changes.
// Simulate the missing event with History handler.
if (BrowserInfo.get().isIE()) {
History.addValueChangeHandler(evt -> {
final String newLocation = Browser.getWindow().getLocation()
.toString();
if (!newLocation.equals(currentLocation)) {
currentLocation = newLocation;
getRpcProxy(UIServerRpc.class).popstate(
Browser.getWindow().getLocation().toString());
}
});
currentLocation = Browser.getWindow().getLocation().toString();
}

}

private native void open(String url, String name)
Expand Down Expand Up @@ -402,34 +431,13 @@ public void execute() {
scrollIntoView(connector);
}

if (uidl.hasAttribute(UIConstants.LOCATION_VARIABLE)) {
String location = uidl
.getStringAttribute(UIConstants.LOCATION_VARIABLE);
String newFragment;

int fragmentIndex = location.indexOf('#');
if (fragmentIndex >= 0) {
// Decode fragment to avoid double encoding (#10769)
newFragment = URL.decodePathSegment(
location.substring(fragmentIndex + 1));

if (newFragment.isEmpty()
&& Location.getHref().indexOf('#') == -1) {
// Ensure there is a trailing # even though History and
// Location.getHash() treat null and "" the same way.
Location.assign(Location.getHref() + "#");
}
} else {
// No fragment in server-side location, but can't completely
// remove the browser fragment since that would reload the page
newFragment = "";
}

getWidget().currentFragment = newFragment;

if (!newFragment.equals(History.getToken())) {
History.newItem(newFragment, true);
}
if (uidl.hasAttribute(UIConstants.ATTRIBUTE_PUSH_STATE)) {
Browser.getWindow().getHistory().pushState(null, "",
uidl.getStringAttribute(UIConstants.ATTRIBUTE_PUSH_STATE));
}
if (uidl.hasAttribute(UIConstants.ATTRIBUTE_REPLACE_STATE)) {
Browser.getWindow().getHistory().replaceState(null, "", uidl
.getStringAttribute(UIConstants.ATTRIBUTE_REPLACE_STATE));
}

if (firstPaint) {
Expand Down
Expand Up @@ -8,8 +8,7 @@

<inherits name="com.vaadin.Vaadin" />

<!-- Elemental is used for handling Json only -->
<inherits name="elemental.Json" />
<inherits name="elemental.Elemental" />

<inherits name="com.google.gwt.precompress.Precompress" />

Expand Down

0 comments on commit 62c44dd

Please sign in to comment.