--- /dev/null
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.terminal.gwt.client;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.impl.HistoryImpl;
+
+/**
+ * A slightly modified version of GWT's HistoryImplIE6 to bypass bug #2931. Also
+ * combined with HistoryImplFrame.
+ *
+ * This class should be removed if GWT issue 3890 gets resolved. (Also remember
+ * to removed deferred binding rule from .gwt.xml file).
+ */
+public class HistoryImplIEVaadin extends HistoryImpl {
+
+ private static native Element findHistoryFrame()
+ /*-{
+ return $doc.getElementById('__gwt_historyFrame');
+ }-*/;
+
+ private static native Element getTokenElement(Element historyFrame)
+ /*-{
+ // Initialize the history iframe. If '__gwt_historyToken' already exists, then
+ // we're probably backing into the app, so _don't_ set the iframe's location.
+ if (historyFrame.contentWindow) {
+ var doc = historyFrame.contentWindow.document;
+ return doc.getElementById('__gwt_historyToken');
+ }
+ }-*/;
+
+ protected Element historyFrame;
+
+ @Override
+ protected final void nativeUpdate(String historyToken) {
+ /*
+ * Must update the location hash since it isn't already correct.
+ */
+ updateHash(historyToken);
+ navigateFrame(historyToken);
+ }
+
+ @Override
+ protected final void nativeUpdateOnEvent(String historyToken) {
+ updateHash(historyToken);
+ }
+
+ /**
+ * Sanitizes an untrusted string to be used in an HTML context. NOTE: This
+ * method of escaping strings should only be used on Internet Explorer.
+ *
+ * @param maybeHtml
+ * untrusted string that may contain html
+ * @return sanitized string
+ */
+ @SuppressWarnings("unused")
+ private static String escapeHtml(String maybeHtml) {
+ final Element div = DOM.createDiv();
+ DOM.setInnerText(div, maybeHtml);
+ return DOM.getInnerHTML(div);
+ }
+
+ /**
+ * For IE6, reading from $wnd.location.hash drops part of the fragment if
+ * the fragment contains a '?'. To avoid this bug, we use location.href
+ * instead.
+ */
+ @SuppressWarnings("unused")
+ private static native String getLocationHash()
+ /*-{
+ var href = $wnd.location.href;
+ var hashLoc = href.lastIndexOf("#");
+ return (hashLoc > 0) ? href.substring(hashLoc) : "";
+ }-*/;
+
+ @Override
+ public boolean init() {
+ historyFrame = findHistoryFrame();
+ if (historyFrame == null) {
+ return false;
+ }
+
+ initHistoryToken();
+
+ // Initialize the history iframe. If a token element already exists,
+ // then
+ // we're probably backing into the app, so _don't_ create a new item.
+ Element tokenElement = getTokenElement(historyFrame);
+ if (tokenElement != null) {
+ setToken(getTokenElementContent(tokenElement));
+ } else {
+ navigateFrame(getToken());
+ }
+
+ injectGlobalHandler();
+
+ initUrlCheckTimer();
+ return true;
+ }
+
+ protected native String getTokenElementContent(Element tokenElement)
+ /*-{
+ return tokenElement.innerText;
+ }-*/;
+
+ protected native void initHistoryToken()
+ /*-{
+ // Assume an empty token.
+ var token = '';
+ // Get the initial token from the url's hash component.
+ var hash = @com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::getLocationHash()();
+ if (hash.length > 0) {
+ try {
+ token = this.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
+ } catch (e) {
+ // Clear the bad hash (this can't have been a valid token).
+ $wnd.location.hash = '';
+ }
+ }
+ @com.google.gwt.user.client.impl.HistoryImpl::setToken(Ljava/lang/String;)(token);
+ }-*/;
+
+ protected native void injectGlobalHandler()
+ /*-{
+ var historyImplRef = this;
+
+ $wnd.__gwt_onHistoryLoad = function(token) {
+ historyImplRef.@com.google.gwt.user.client.impl.HistoryImpl::newItemOnEvent(Ljava/lang/String;)(token);
+ };
+ }-*/;
+
+ protected native void navigateFrame(String token)
+ /*-{
+ var escaped = @com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::escapeHtml(Ljava/lang/String;)(token);
+ var doc = this.@com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::historyFrame.contentWindow.document;
+ doc.open();
+ doc.write('<html><body onload="if(parent.__gwt_onHistoryLoad)parent.__gwt_onHistoryLoad(__gwt_historyToken.innerText)"><div id="__gwt_historyToken">' + escaped + '</div></body></html>');
+ doc.close();
+ }-*/;
+
+ protected native void updateHash(String token)
+ /*-{
+ $wnd.location.hash = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(token);
+ }-*/;
+
+ private native void initUrlCheckTimer()
+ /*-{
+ // This is the URL check timer. It detects when an unexpected change
+ // occurs in the document's URL (e.g. when the user enters one manually
+ // or selects a 'favorite', but only the #hash part changes). When this
+ // occurs, we _must_ reload the page. This is because IE has a really
+ // nasty bug that totally mangles its history stack and causes the location
+ // bar in the UI to stop working under these circumstances.
+ var historyImplRef = this;
+ var urlChecker = function() {
+ $wnd.setTimeout(urlChecker, 250);
+ var hash = @com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::getLocationHash()();
+ if (hash.length > 0) {
+ var token = '';
+ try {
+ token = historyImplRef.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
+ } catch (e) {
+ // If there's a bad hash, always reload. This could only happen if
+ // if someone entered or linked to a bad url.
+ $wnd.location.reload();
+ }
+
+ var historyToken = @com.google.gwt.user.client.impl.HistoryImpl::getToken()();
+ if (token != historyToken) {
+ $wnd.location.reload();
+ }
+ }
+ };
+ urlChecker();
+ }-*/;
+
+}