encode the history token before putting it in the href for Hyperlink.
matches to decode of history token in initialization of History.
fixes bug where the history token would appear to be decoded an extra time.
The TAP Presubmit ID for this request is: OCL:26777068:BASE:26781201:1326411820853:f9de5cd6
Results can be found here:
http://test/OCL%3A26777068%3ABASE%3A26781201%3A1326411820853%3Af9de5cd6
Finished testing change 26777068 with 1086 passing, 5 skipped, 0 failed/broken, 0 other statuses
Review by: jat@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10832 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/History.java b/user/src/com/google/gwt/user/client/History.java
index 8076f11..7507570 100644
--- a/user/src/com/google/gwt/user/client/History.java
+++ b/user/src/com/google/gwt/user/client/History.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* 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
@@ -26,18 +26,18 @@
* "token". You can create new history items (which have a token associated with
* them when they are created), and you can programmatically force the current
* history to move back or forward.
- *
+ *
* <p>
* In order to receive notification of user-directed changes to the current
* history item, implement the {@link ValueChangeHandler} interface and attach
* it via {@link #addValueChangeHandler(ValueChangeHandler)}.
* </p>
- *
+ *
* <p>
* <h3>Example</h3>
* {@example com.google.gwt.examples.HistoryExample}
* </p>
- *
+ *
* <p>
* <h3>URL Encoding</h3>
* Any valid characters may be used in the history token and will survive
@@ -75,7 +75,7 @@
/**
* Adds a listener to be informed of changes to the browser's history stack.
- *
+ *
* @param listener the listener to be added
* @deprecated use {@link History#addValueChangeHandler(ValueChangeHandler)} instead
*/
@@ -89,7 +89,7 @@
/**
* Adds a {@link com.google.gwt.event.logical.shared.ValueChangeEvent} handler
* to be informed of changes to the browser's history stack.
- *
+ *
* @param handler the handler
* @return the registration used to remove this value change handler
*/
@@ -100,7 +100,7 @@
/**
* Programmatic equivalent to the user pressing the browser's 'back' button.
- *
+ *
* Note that this does not work correctly on Safari 2.
*/
public static native void back() /*-{
@@ -108,6 +108,16 @@
}-*/;
/**
+ * Encode a history token for use as part of a URI.
+ *
+ * @param historyToken the token to encode
+ * @return the encoded token, suitable for use as part of a URI
+ */
+ public static String encodeHistoryToken(String historyToken) {
+ return impl != null ? impl.encodeFragment(historyToken) : historyToken;
+ }
+
+ /**
* Fire
* {@link ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent)}
* events with the current history state. This is most often called at the end
@@ -136,7 +146,7 @@
* event for the initial token; requiring that an application request the
* token explicitly on startup gives it an opportunity to run different
* initialization code in the presence or absence of an initial token.
- *
+ *
* @return the initial token, or the empty string if none is present.
*/
public static String getToken() {
@@ -147,7 +157,7 @@
* Adds a new browser history entry. Calling this method will cause
* {@link ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent)}
* to be called as well.
- *
+ *
* @param historyToken the token to associate with the new history item
*/
public static void newItem(String historyToken) {
@@ -158,7 +168,7 @@
* Adds a new browser history entry. Calling this method will cause
* {@link ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent)}
* to be called as well if and only if issueEvent is true.
- *
+ *
* @param historyToken the token to associate with the new history item
* @param issueEvent true if a
* {@link ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent)}
@@ -177,7 +187,7 @@
* application, instead call {@link #fireCurrentHistoryState()} from the
* application {@link com.google.gwt.core.client.EntryPoint#onModuleLoad()}
* method.
- *
+ *
* @param historyToken history token to fire events for
* @deprecated Use {@link #fireCurrentHistoryState()} instead.
*/
@@ -190,7 +200,7 @@
/**
* Removes a history listener.
- *
+ *
* @param listener the listener to be removed
*/
@Deprecated
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImpl.java b/user/src/com/google/gwt/user/client/impl/HistoryImpl.java
index 0e71be8..8406763 100644
--- a/user/src/com/google/gwt/user/client/impl/HistoryImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImpl.java
@@ -71,6 +71,11 @@
return handlers.addHandler(ValueChangeEvent.getType(), handler);
}
+ public native String encodeFragment(String fragment) /*-{
+ // encodeURI() does *not* encode the '#' character.
+ return encodeURI(fragment).replace("#", "%23");
+ }-*/;
+
public void fireEvent(GwtEvent<?> event) {
handlers.fireEvent(event);
}
@@ -142,11 +147,6 @@
return decodeURI(encodedFragment.replace("%23", "#"));
}-*/;
- protected native String encodeFragment(String fragment) /*-{
- // encodeURI() does *not* encode the '#' character.
- return encodeURI(fragment).replace("#", "%23");
- }-*/;
-
/**
* The standard updateHash implementation assigns to location.hash() with an
* encoded history token.
diff --git a/user/src/com/google/gwt/user/client/ui/Hyperlink.java b/user/src/com/google/gwt/user/client/ui/Hyperlink.java
index 08e25f8..61b9821 100644
--- a/user/src/com/google/gwt/user/client/ui/Hyperlink.java
+++ b/user/src/com/google/gwt/user/client/ui/Hyperlink.java
@@ -342,7 +342,8 @@
assert targetHistoryToken != null
: "targetHistoryToken must not be null, consider using Anchor instead";
this.targetHistoryToken = targetHistoryToken;
- DOM.setElementProperty(anchorElem, "href", "#" + targetHistoryToken);
+ String hash = History.encodeHistoryToken(targetHistoryToken);
+ DOM.setElementProperty(anchorElem, "href", "#" + hash);
}
public void setText(String text) {
diff --git a/user/test/com/google/gwt/user/client/ui/HyperlinkTest.java b/user/test/com/google/gwt/user/client/ui/HyperlinkTest.java
index f97c6df..6d2c5dc 100644
--- a/user/test/com/google/gwt/user/client/ui/HyperlinkTest.java
+++ b/user/test/com/google/gwt/user/client/ui/HyperlinkTest.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* 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
@@ -15,16 +15,24 @@
*/
package com.google.gwt.user.client.ui;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.History;
+import com.google.web.bindery.event.shared.HandlerRegistration;
/**
* Tests {@link HyperlinkTest}.
*/
public class HyperlinkTest extends GWTTestCase {
- static final String html = "<b>hello</b><i>world</i>";
+ private static final String TEST_HTML = "<b>hello</b><i>world</i>";
+ private static final String TEST_HISTORY_TOKEN = "myToken %~`!:{}%20+%37#>";
@Override
public String getModuleName() {
@@ -32,24 +40,61 @@
}
public void testDebugId() {
- Hyperlink link = new Hyperlink("Click Me", "myToken");
+ Hyperlink link = new Hyperlink("Click Me", TEST_HISTORY_TOKEN);
link.ensureDebugId("myLink");
UIObjectTest.assertDebugId("myLink-wrapper", link.getElement());
UIObjectTest.assertDebugId("myLink", DOM.getFirstChild(link.getElement()));
}
public void testSafeHtmlConstructor() {
- String token = "myToken";
- Hyperlink link = new Hyperlink(SafeHtmlUtils.fromSafeConstant(html), token);
-
- assertEquals(html, link.getHTML().toLowerCase());
+ Hyperlink link = new Hyperlink(SafeHtmlUtils.fromSafeConstant(TEST_HTML), TEST_HISTORY_TOKEN);
+ assertEquals(TEST_HTML, link.getHTML().toLowerCase());
}
public void testSetSafeHtml() {
- String token = "myToken";
- Hyperlink link = new Hyperlink("foobar", token);
- link.setHTML(SafeHtmlUtils.fromSafeConstant(html));
-
- assertEquals(html, link.getHTML().toLowerCase());
+ Hyperlink link = new Hyperlink("foobar", TEST_HISTORY_TOKEN);
+ link.setHTML(SafeHtmlUtils.fromSafeConstant(TEST_HTML));
+ assertEquals(TEST_HTML, link.getHTML().toLowerCase());
+ }
+
+ public void testLinkToken() {
+ Hyperlink link = new Hyperlink("foobar", TEST_HISTORY_TOKEN);
+ assertEquals(TEST_HISTORY_TOKEN, link.getTargetHistoryToken());
+ }
+
+ public void testLinkHrefProperty() {
+ Hyperlink link = new Hyperlink("foobar", TEST_HISTORY_TOKEN);
+ Element element = link.getElement();
+ Element anchorElement = (Element)element.getFirstChildElement();
+ String propertyString = DOM.getElementProperty(anchorElement, "href");
+ int index = propertyString.indexOf('#');
+ assertFalse(index == -1);
+ String fragment = propertyString.substring(index + 1);
+ String expected = History.encodeHistoryToken(TEST_HISTORY_TOKEN);
+ assertEquals(expected, fragment);
+ }
+
+ public void testLinkTraversal() {
+ final String testHistoryToken = TEST_HISTORY_TOKEN;
+ Hyperlink link = new Hyperlink("foobar", testHistoryToken);
+ HandlerRegistration registration = null;
+ try {
+ RootPanel.get().add(link);
+ registration = History.addValueChangeHandler(new ValueChangeHandler<String>() {
+ @Override
+ public void onValueChange(ValueChangeEvent<String> event) {
+ assertEquals(testHistoryToken, event.getValue());
+ assertEquals(testHistoryToken, History.getToken());
+ }
+ });
+ Document document = Document.get();
+ NativeEvent event = document.createClickEvent(1, 0, 0, 0, 0, false, false, false, false);
+ link.getElement().dispatchEvent(event);
+ } finally {
+ RootPanel.get().remove(link);
+ if (registration != null) {
+ registration.removeHandler();
+ }
+ }
}
}