diff options
134 files changed, 3900 insertions, 830 deletions
diff --git a/WebContent/VAADIN/themes/base/absolutelayout/absolutelayout.css b/WebContent/VAADIN/themes/base/absolutelayout/absolutelayout.css index 0d2f7312fb..637d829d78 100644 --- a/WebContent/VAADIN/themes/base/absolutelayout/absolutelayout.css +++ b/WebContent/VAADIN/themes/base/absolutelayout/absolutelayout.css @@ -15,8 +15,9 @@ overflow: hidden; } .v-absolutelayout-margin, .v-absolutelayout-canvas { - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-absolutelayout.v-has-height > div, .v-absolutelayout.v-has-height > div > div { height: 100%; diff --git a/WebContent/VAADIN/themes/base/common/common.css b/WebContent/VAADIN/themes/base/common/common.css index 6de2b26267..3c08a9d584 100644 --- a/WebContent/VAADIN/themes/base/common/common.css +++ b/WebContent/VAADIN/themes/base/common/common.css @@ -97,8 +97,9 @@ div.v-app-loading { } .v-form-content { height: 100%; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } /* Field modified */ /* Disabled by default diff --git a/WebContent/VAADIN/themes/base/csslayout/csslayout.css b/WebContent/VAADIN/themes/base/csslayout/csslayout.css index 2713d621a2..40cec4889f 100644 --- a/WebContent/VAADIN/themes/base/csslayout/csslayout.css +++ b/WebContent/VAADIN/themes/base/csslayout/csslayout.css @@ -6,8 +6,8 @@ div.v-csslayout { display: block; } .v-csslayout-margin, .v-csslayout-container { - -moz-box-sizing: border-box; -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; box-sizing: border-box; } .v-has-width > .v-csslayout-margin, diff --git a/WebContent/VAADIN/themes/base/panel/panel.css b/WebContent/VAADIN/themes/base/panel/panel.css index 286b1b7f4d..489d6bc015 100644 --- a/WebContent/VAADIN/themes/base/panel/panel.css +++ b/WebContent/VAADIN/themes/base/panel/panel.css @@ -30,8 +30,9 @@ } .v-panel-content { overflow: auto; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-panel.v-has-width > .v-panel-content { width: 100%; diff --git a/WebContent/VAADIN/themes/base/select/select.css b/WebContent/VAADIN/themes/base/select/select.css index fe50b98ce6..b9d0dda51f 100644 --- a/WebContent/VAADIN/themes/base/select/select.css +++ b/WebContent/VAADIN/themes/base/select/select.css @@ -67,8 +67,9 @@ margin: 0; float: left; -webkit-border-radius: 0px; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-filterselect-prompt .v-filterselect-input { color: #999; diff --git a/WebContent/VAADIN/themes/base/window/window.css b/WebContent/VAADIN/themes/base/window/window.css index f553f95fdf..d728e7f60e 100644 --- a/WebContent/VAADIN/themes/base/window/window.css +++ b/WebContent/VAADIN/themes/base/window/window.css @@ -2,8 +2,9 @@ background: #fff; } .v-window-contents { - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-window.v-has-width > div.popupContent, @@ -24,8 +25,9 @@ padding: 0.3em 1em; height: 1.6em; position: relative; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-window-outerheader, diff --git a/WebContent/VAADIN/themes/reindeer/datefield/datefield.css b/WebContent/VAADIN/themes/reindeer/datefield/datefield.css index bc8f617845..1ececf9fb4 100644 --- a/WebContent/VAADIN/themes/reindeer/datefield/datefield.css +++ b/WebContent/VAADIN/themes/reindeer/datefield/datefield.css @@ -250,8 +250,9 @@ td.v-datefield-calendarpanel-nextyear { -webkit-border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; height: 23px; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-datefield.v-readonly input.v-datefield-textfield { border-right-width: 1px; diff --git a/WebContent/VAADIN/themes/reindeer/select/select.css b/WebContent/VAADIN/themes/reindeer/select/select.css index 903066e4ab..8962862702 100644 --- a/WebContent/VAADIN/themes/reindeer/select/select.css +++ b/WebContent/VAADIN/themes/reindeer/select/select.css @@ -18,8 +18,9 @@ .v-window input.v-filterselect-input, .v-popupview-popup input.v-filterselect-input { padding: 4px 0 4px 2px; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-filterselect-prompt .v-filterselect-input { font-style: normal; diff --git a/WebContent/VAADIN/themes/reindeer/window/window.css b/WebContent/VAADIN/themes/reindeer/window/window.css index 3015f70eb5..e1091ef98d 100644 --- a/WebContent/VAADIN/themes/reindeer/window/window.css +++ b/WebContent/VAADIN/themes/reindeer/window/window.css @@ -3,8 +3,9 @@ } .v-window-wrap { border: 1px solid #808386; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-sa .v-window-wrap, .v-op .v-window-wrap { diff --git a/WebContent/VAADIN/themes/runo/window/window.css b/WebContent/VAADIN/themes/runo/window/window.css index d412f66605..675c8942bf 100644 --- a/WebContent/VAADIN/themes/runo/window/window.css +++ b/WebContent/VAADIN/themes/runo/window/window.css @@ -27,8 +27,9 @@ border: 2px solid #babfc0; border-top: none; border-bottom: none; - box-sizing: border-box; + -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + box-sizing: border-box; } .v-window div.v-window-footer { height: 8px; diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html index 1ca217bc56..1b56b7fb9c 100644 --- a/WebContent/release-notes.html +++ b/WebContent/release-notes.html @@ -71,16 +71,34 @@ <h2 id="enhancements">Enhancements in Vaadin @version-minor@</h2> <p> - @version-minor@ is the first development release of the upcoming Version 7 of the - Vaadin Framework. It introduces the first set of new features in Vaadin 7, for the - purpose of receiving feedback regarding the changes. + @version-minor@ is the second development release of the upcoming Version 7 of the + Vaadin Framework. It introduces the second set of new features in Vaadin 7, for the + purpose of receiving feedback about the changes. </p> - <p>The major changes in this first phase are: + <p>The major changes in this second alpha phase are: <ul> - <li>Redesign of the window and application APIs</li> - <li>Redesign of forms and data binding</li> + <li>Complete overhaul of the client-server communication architecture + <ul> + <li>All add-on components that have widgets need to be ported to Vaadin 7</li> + <li>Integration of a GWT widget is done in a <i>connector</i> class</li> + <li>Component-to-widget mapping now defined on the client-side, in the connector</li> + <li>No more <b style="text-decoration: line-through">Paintable</b> or <b style="text-decoration: line-through">VariableOwner</b> + <li>Server-side component and client-side widget can have a <emphasis>shared state</emphasis> object which is automatically synchronized</li> + <li>Both client-side and server-side can make RPC calls to the other side + <ul> + <li>Communicated in the next request/response</li> + <li>No return values + <li>Typically for communicating events</li> + </ul> + </li> + <li>UIDL is deprecated</li> + <li>Compatibility layer for Vaadin 6 included for easy migration</li> + </ul> + </li> + <li>Get computed style of a component from the browser</li> + <li>Support for border, padding, and margin in core layout components</li> </ul> <p> @@ -93,7 +111,7 @@ <p> For a complete list of changes in this release, please see the <a - href="http://dev.vaadin.com/query?status=closed&group=resolution&milestone=Vaadin+7.0.0.alpha1">list + href="http://dev.vaadin.com/query?status=closed&group=resolution&milestone=Vaadin+7.0.0.alpha2">list of closed tickets</a>. </p> @@ -106,6 +124,17 @@ </p> <p> + Vaadin 6 add-ons (ones that contain widgets) do not work in Vaadin 7 - please + check the add-ons in <a href="http://vaadin.com/directory/">Vaadin Directory</a> + for Vaadin 7 support. + </p> + + <p> + Any custom client-side widgets need to be changed to use the new client-server + communication API or the Vaadin 6 compatibility API. + </p> + + <p> A detailed list of migration changes are given in the <a href="http://dev.vaadin.com/wiki/Vaadin7/MigrationGuide">Vaadin 7 Migration Guide</a>. @@ -288,8 +317,7 @@ </p> <ul> - <li>Windows (see the <a href="#knownissues">Zip installation - notice above</a>)</li> + <li>Windows</li> <li>Linux</li> <li>Mac OS X</li> </ul> @@ -330,11 +358,11 @@ </p> <ul> - <li>Mozilla Firefox 10</li> + <li>Mozilla Firefox 11</li> <li>Internet Explorer 8-9</li> <li>Safari 5</li> <li>Opera 11</li> - <li>Google Chrome 16</li> + <li>Google Chrome 18</li> </ul> <h2 id="vaadinontheweb">Vaadin on the Web</h2> diff --git a/WebContent/statictestfiles/browserfeatures/fullHeightScrollbar.html b/WebContent/statictestfiles/browserfeatures/fullHeightScrollbar.html new file mode 100644 index 0000000000..2e280da64e --- /dev/null +++ b/WebContent/statictestfiles/browserfeatures/fullHeightScrollbar.html @@ -0,0 +1,59 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> +<style type="text/css"> +.wrapper { + height: 150px; + width: 150px; + border: 1px solid black; + overflow: auto; + position: relative; +} + +.content { + height: 100%; + width: 250px; + background: grey; +} + +</style> +<script type="text/javascript"> +function disableScrolling() { + var result = document.getElementsByClassName("content"); + for(var i = 0; i < result.length; i++) { + var e = result[i]; + e.style.width = "100%"; + } +} + +function triggerReflow() { + var style = "top"; + var styleValue = "1px"; + var result = document.getElementsByClassName("wrapper"); + for(var i = 0; i < result.length; i++) { + var e = result[i]; + var originalValue = e.style[style]; + e.style[style] = styleValue; + e.offsetWidth; + e.style[style] = originalValue; + } +} +</script> +</head> +<body scroll="auto"> +<p>This test is used to verify how browsers take horizontal scrollbars into account when calculating 100% height and what happens when scrolling is no longer needed. This test tells which browsers need which workarounds for related features.</p> + +<p>Basic situation. +<div class="wrapper"><div class="content"></div></div> +</p> + +<p> +Situation with position: absolute on the inner element. +<div class="wrapper"><div class="content" style="position: absolute"></div></div> +</p> + +<button id="disableScrolling" onclick="disableScrolling()">Disable scrolling</button> +<button id="triggerReflow" onclick="triggerReflow()">Trigger reflow</button> +</body> +</html> diff --git a/build/build.xml b/build/build.xml index 08423c276b..573c799f7d 100644 --- a/build/build.xml +++ b/build/build.xml @@ -364,6 +364,13 @@ </fileset> </copy> + <!-- Add test files to be included in test war --> + <copy todir="${output-dir}/WebContent"> + <fileset dir="WebContent"> + <include name="statictestfiles/**" /> + </fileset> + </copy> + <!-- Add servlet and portlet configuration files from WebContent --> <copy todir="${output-dir}/WebContent/WEB-INF"> <fileset dir="WebContent/WEB-INF"> @@ -838,7 +845,7 @@ <property file="${gpg.passphrase.file}" /> <echo>Publishing ${output-dir}/WebContent/WEB-INF/lib/${lib-jar-name} to Maven repository</echo> - <artifact:mvn> + <artifact:mvn failonerror="true"> <arg value="gpg:sign-and-deploy-file"/> <!-- .. is a workaround as maven runs in the build directory --> <sysproperty key="file" value="../${output-dir}/WebContent/WEB-INF/lib/${lib-jar-name}" /> diff --git a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java index 91950f5d4f..9159fa358b 100644 --- a/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java +++ b/src/com/vaadin/data/util/ContainerHierarchicalWrapper.java @@ -294,7 +294,8 @@ public class ContainerHierarchicalWrapper implements Container.Hierarchical, return ((Container.Hierarchical) container).hasChildren(itemId); } - return children.get(itemId) != null; + LinkedList<Object> list = children.get(itemId); + return (list != null && !list.isEmpty()); } /* diff --git a/src/com/vaadin/data/util/IndexedContainer.java b/src/com/vaadin/data/util/IndexedContainer.java index 1e0a2fae1a..bcaa5eda42 100644 --- a/src/com/vaadin/data/util/IndexedContainer.java +++ b/src/com/vaadin/data/util/IndexedContainer.java @@ -865,7 +865,6 @@ public class IndexedContainer extends * @see com.vaadin.data.Property#setValue(java.lang.Object) */ public void setValue(Object newValue) throws Property.ReadOnlyException { - // Gets the Property set final Map<Object, Object> propertySet = items.get(itemId); @@ -875,8 +874,10 @@ public class IndexedContainer extends } else if (getType().isAssignableFrom(newValue.getClass())) { propertySet.put(propertyId, newValue); } else { - throw new IllegalArgumentException("Value is of invalid type, " - + getType().getName() + " expected"); + throw new IllegalArgumentException( + "Value is of invalid type, got " + + newValue.getClass().getName() + " but " + + getType().getName() + " was expected"); } // update the container filtering if this property is being filtered diff --git a/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java b/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java index 8562bc70ec..0d29e9a327 100644 --- a/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java +++ b/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java @@ -6,6 +6,9 @@ */ package com.vaadin.event.dd.acceptcriteria; +import java.util.logging.Level; +import java.util.logging.Logger; + import com.vaadin.event.TransferableImpl; import com.vaadin.event.dd.DragAndDropEvent; import com.vaadin.terminal.PaintException; @@ -22,27 +25,39 @@ import com.vaadin.ui.Component; @SuppressWarnings("serial") @ClientCriterion(VDragSourceIs.class) public class SourceIs extends ClientSideCriterion { + private static final Logger logger = Logger.getLogger(SourceIs.class + .getName()); - private Component[] component; + private Component[] components; public SourceIs(Component... component) { - this.component = component; + components = component; } @Override public void paintContent(PaintTarget target) throws PaintException { super.paintContent(target); - target.addAttribute("c", component.length); - for (int i = 0; i < component.length; i++) { - target.addAttribute("component" + i, component[i]); + int paintedComponents = 0; + for (int i = 0; i < components.length; i++) { + Component c = components[i]; + if (c.getApplication() != null) { + target.addAttribute("component" + paintedComponents++, c); + } else { + logger.log( + Level.WARNING, + "SourceIs component {0} at index {1} is not attached to the component hierachy and will thus be ignored", + new Object[] { c.getClass().getName(), + Integer.valueOf(i) }); + } } + target.addAttribute("c", paintedComponents); } public boolean accept(DragAndDropEvent dragEvent) { if (dragEvent.getTransferable() instanceof TransferableImpl) { Component sourceComponent = ((TransferableImpl) dragEvent .getTransferable()).getSourceComponent(); - for (Component c : component) { + for (Component c : components) { if (c == sourceComponent) { return true; } diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java index 170e949116..ff77c5904a 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java @@ -69,6 +69,7 @@ public class ApplicationConfiguration implements EntryPoint { if (value === null || value === undefined) { return null; } else { + // $entry not needed as function is not exported return @java.lang.Boolean::valueOf(Z)(value); } }-*/; @@ -89,6 +90,7 @@ public class ApplicationConfiguration implements EntryPoint { if (value === null || value === undefined) { return null; } else { + // $entry not needed as function is not exported return @java.lang.Integer::valueOf(I)(value); } }-*/; @@ -589,7 +591,7 @@ public class ApplicationConfiguration implements EntryPoint { */ public native static void registerCallback(String widgetsetName) /*-{ - var callbackHandler = @com.vaadin.terminal.gwt.client.ApplicationConfiguration::startApplication(Ljava/lang/String;); + var callbackHandler = $entry(@com.vaadin.terminal.gwt.client.ApplicationConfiguration::startApplication(Ljava/lang/String;)); $wnd.vaadin.registerWidgetset(widgetsetName, callbackHandler); }-*/; diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 2d9398320e..739c232a72 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -256,10 +256,10 @@ public class ApplicationConnection { /*-{ var ap = this; var client = {}; - client.isActive = function() { + client.isActive = $entry(function() { return ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::hasActiveRequest()() || ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::isExecutingDeferredCommands()(); - } + }); var vi = ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::getVersionInfo()(); if (vi) { client.getVersionInfo = function() { @@ -267,12 +267,21 @@ public class ApplicationConnection { } } - client.getElementByPath = function(id) { + client.getProfilingData = $entry(function() { + var pd = [ + ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::lastProcessingTime, + ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::totalProcessingTime + ]; + pd = pd.concat(ap.@com.vaadin.terminal.gwt.client.ApplicationConnection::serverTimingInfo); + return pd; + }); + + client.getElementByPath = $entry(function(id) { return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getElementByPath(Ljava/lang/String;)(id); - } - client.getPathForElement = function(element) { + }); + client.getPathForElement = $entry(function(element) { return componentLocator.@com.vaadin.terminal.gwt.client.ComponentLocator::getPathForElement(Lcom/google/gwt/user/client/Element;)(element); - } + }); $wnd.vaadin.clients[TTAppId] = client; }-*/; @@ -314,22 +323,22 @@ public class ApplicationConnection { if ($wnd.vaadin.forceSync) { oldSync = $wnd.vaadin.forceSync; } - $wnd.vaadin.forceSync = function() { + $wnd.vaadin.forceSync = $entry(function() { if (oldSync) { oldSync(); } app.@com.vaadin.terminal.gwt.client.ApplicationConnection::sendPendingVariableChanges()(); - } + }); var oldForceLayout; if ($wnd.vaadin.forceLayout) { oldForceLayout = $wnd.vaadin.forceLayout; } - $wnd.vaadin.forceLayout = function() { + $wnd.vaadin.forceLayout = $entry(function() { if (oldForceLayout) { oldForceLayout(); } app.@com.vaadin.terminal.gwt.client.ApplicationConnection::forceLayout()(); - } + }); }-*/; /** @@ -536,21 +545,22 @@ public class ApplicationConnection { return; case 503: - // We'll assume msec instead of the usual seconds - int delay = Integer.parseInt(response - .getHeader("Retry-After")); - VConsole.log("503, retrying in " + delay + "msec"); - (new Timer() { - @Override - public void run() { - // TODO why? Here used to be "activeRequests--;" - // but can't see why exactly - hasActiveRequest = false; - doUidlRequest(uri, payload, synchronous); - } - }).schedule(delay); - return; - + /* + * We'll assume msec instead of the usual seconds. If + * there's no Retry-After header, handle the error like + * a 500, as per RFC 2616 section 10.5.4. + */ + String delay = response.getHeader("Retry-After"); + if (delay != null) { + VConsole.log("503, retrying in " + delay + "msec"); + (new Timer() { + @Override + public void run() { + doUidlRequest(uri, payload, synchronous); + } + }).schedule(Integer.parseInt(delay)); + return; + } } if ((statusCode / 100) == 4) { @@ -561,6 +571,13 @@ public class ApplicationConnection { + statusCode, statusCode); endRequest(); return; + } else if ((statusCode / 100) == 5) { + // Something's wrong on the server, there's nothing the + // client can do except maybe try again. + showCommunicationError("Server error. Error code: " + + statusCode, statusCode); + endRequest(); + return; } String contentType = response.getHeader("Content-Type"); @@ -663,6 +680,26 @@ public class ApplicationConnection { } int cssWaits = 0; + + /** + * Holds the time spent rendering the last request + */ + protected int lastProcessingTime; + + /** + * Holds the total time spent rendering requests during the lifetime of the + * session. + */ + protected int totalProcessingTime; + + /** + * Holds the timing information from the server-side. How much time was + * spent servicing the last request and how much time has been spent + * servicing the session so far. These values are always one request behind, + * since they cannot be measured before the request is finished. + */ + private ValueMap serverTimingInfo; + static final int MAX_CSS_WAITS = 100; protected void handleWhenCSSLoaded(final String jsonText, @@ -999,6 +1036,12 @@ public class ApplicationConnection { handleUIDLDuration.logDuration( " * Handling type mappings from server completed", 10); + /* + * Hook for e.g. TestBench to get details about server peformance + */ + if (json.containsKey("timings")) { + serverTimingInfo = json.getValueMap("timings"); + } Command c = new Command() { public void execute() { @@ -1183,10 +1226,12 @@ public class ApplicationConnection { // TODO build profiling for widget impl loading time - final long prosessingTime = (new Date().getTime()) - - start.getTime(); + lastProcessingTime = (int) ((new Date().getTime()) - start + .getTime()); + totalProcessingTime += lastProcessingTime; + VConsole.log(" Processing time was " - + String.valueOf(prosessingTime) + "ms for " + + String.valueOf(lastProcessingTime) + "ms for " + jsonText.length() + " characters of JSON"); VConsole.log("Referenced paintables: " + connectorMap.size()); @@ -2367,6 +2412,10 @@ public class ApplicationConnection { return true; } + if (!manageCaption) { + VConsole.error(Util.getConnectorString(connector) + + " called updateComponent with manageCaption=false. The parameter was ignored - override delegateCaption() to return false instead. It is however not recommended to use caption this way at all."); + } return false; } diff --git a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java index ef1dc481b1..064f175301 100644 --- a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java +++ b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java @@ -28,6 +28,8 @@ public class BrowserInfo { private static final String OS_WINDOWS = "win"; private static final String OS_LINUX = "lin"; private static final String OS_MACOSX = "mac"; + private static final String OS_ANDROID = "android"; + private static final String OS_IOS = "ios"; private static BrowserInfo instance; @@ -167,13 +169,18 @@ public class BrowserInfo { if (osClass != null) { cssClass = cssClass + " " + prefix + osClass; } + } return cssClass; } private String getOperatingSystemClass() { - if (browserDetails.isWindows()) { + if (browserDetails.isAndroid()) { + return OS_ANDROID; + } else if (browserDetails.isIOS()) { + return OS_IOS; + } else if (browserDetails.isWindows()) { return OS_WINDOWS; } else if (browserDetails.isLinux()) { return OS_LINUX; @@ -312,4 +319,59 @@ public class BrowserInfo { && Util.getNativeScrollbarSize() > 0; } + /** + * Checks if the browser is run on iOS + * + * @return true if the browser is run on iOS, false otherwise + */ + public boolean isIOS() { + return browserDetails.isIOS(); + } + + /** + * Checks if the browser is run on Android + * + * @return true if the browser is run on Android, false otherwise + */ + public boolean isAndroid() { + return browserDetails.isAndroid(); + } + + /** + * Checks if the browser is capable of handling scrolling natively or if a + * touch scroll helper is needed for scrolling. + * + * @return true if browser needs a touch scroll helper, false if the browser + * can handle scrolling natively + */ + public boolean requiresTouchScrollDelegate() { + if (!isTouchDevice()) { + return false; + } + + if (isAndroid() && isWebkit() && getWebkitVersion() < 534) { + return true; + } + // if (isIOS() && isWebkit() && getWebkitVersion() < ???) { + // return true; + // } + + return false; + } + + /** + * Tests if this is an Android devices with a broken scrollTop + * implementation + * + * @return true if scrollTop cannot be trusted on this device, false + * otherwise + */ + public boolean isAndroidWithBrokenScrollTop() { + return isAndroid() + && (getOperatingSystemMajorVersion() == 3 || getOperatingSystemMajorVersion() == 4); + } + + private int getOperatingSystemMajorVersion() { + return browserDetails.getOperatingSystemMajorVersion(); + } } diff --git a/src/com/vaadin/terminal/gwt/client/CSSRule.java b/src/com/vaadin/terminal/gwt/client/CSSRule.java index 4d9196c8d6..c36b0611e8 100644 --- a/src/com/vaadin/terminal/gwt/client/CSSRule.java +++ b/src/com/vaadin/terminal/gwt/client/CSSRule.java @@ -33,6 +33,7 @@ public class CSSRule { for(var i = 0; i < sheets.length; i++) { var sheet = sheets[i]; if(sheet.href && sheet.href.indexOf("VAADIN/themes")>-1) { + // $entry not needed as function is not exported this.@com.vaadin.terminal.gwt.client.CSSRule::rules = @com.vaadin.terminal.gwt.client.CSSRule::searchForRule(Lcom/google/gwt/core/client/JavaScriptObject;Ljava/lang/String;Z)(sheet, selector, deep); return; } @@ -58,6 +59,7 @@ public class CSSRule { // IE handles imported sheet differently if(deep && sheet.imports && sheet.imports.length > 0) { for(var i=0; i < sheet.imports.length; i++) { + // $entry not needed as function is not exported var imports = @com.vaadin.terminal.gwt.client.CSSRule::searchForRule(Lcom/google/gwt/core/client/JavaScriptObject;Ljava/lang/String;Z)(sheet.imports[i], selector, deep); allMatches.concat(imports); } @@ -83,6 +85,7 @@ public class CSSRule { } } else if(deep && r.type == 3) { // Search @import stylesheet + // $entry not needed as function is not exported var imports = @com.vaadin.terminal.gwt.client.CSSRule::searchForRule(Lcom/google/gwt/core/client/JavaScriptObject;Ljava/lang/String;Z)(r.styleSheet, selector, deep); allMatches = allMatches.concat(imports); } @@ -102,6 +105,7 @@ public class CSSRule { /*-{ var j = this.@com.vaadin.terminal.gwt.client.CSSRule::rules.length; for(var i=0; i<j; i++) { + // $entry not needed as function is not exported var value = this.@com.vaadin.terminal.gwt.client.CSSRule::rules[i].style[propertyName]; if(value) return value; diff --git a/src/com/vaadin/terminal/gwt/client/ComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ComponentConnector.java index 34f3f13f03..5f9171084e 100644 --- a/src/com/vaadin/terminal/gwt/client/ComponentConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ComponentConnector.java @@ -119,4 +119,12 @@ public interface ComponentConnector extends ServerConnector { */ public boolean delegateCaptionHandling(); + /** + * Sets the enabled state of the widget associated to this connector. + * + * @param widgetEnabled + * true if the widget should be enabled, false otherwise + */ + public void setWidgetEnabled(boolean widgetEnabled); + } diff --git a/src/com/vaadin/terminal/gwt/client/ComponentDetailMap.java b/src/com/vaadin/terminal/gwt/client/ComponentDetailMap.java index 3405d838a7..dfbcf9d38b 100644 --- a/src/com/vaadin/terminal/gwt/client/ComponentDetailMap.java +++ b/src/com/vaadin/terminal/gwt/client/ComponentDetailMap.java @@ -62,6 +62,7 @@ final class ComponentDetailMap extends JavaScriptObject { private final native void fillWithValues(Collection<ComponentDetail> list) /*-{ for(var key in this) { + // $entry not needed as function is not exported list.@java.util.Collection::add(Ljava/lang/Object;)(this[key]); } }-*/; diff --git a/src/com/vaadin/terminal/gwt/client/ComputedStyle.java b/src/com/vaadin/terminal/gwt/client/ComputedStyle.java index 7dc937cee7..76f2328711 100644 --- a/src/com/vaadin/terminal/gwt/client/ComputedStyle.java +++ b/src/com/vaadin/terminal/gwt/client/ComputedStyle.java @@ -179,6 +179,7 @@ public class ComputedStyle { if (isNaN(number)) return null; else + // $entry not needed as function is not exported return @java.lang.Integer::valueOf(I)(number); }-*/; diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManager.java b/src/com/vaadin/terminal/gwt/client/LayoutManager.java index 55d3fbe272..79a0030140 100644 --- a/src/com/vaadin/terminal/gwt/client/LayoutManager.java +++ b/src/com/vaadin/terminal/gwt/client/LayoutManager.java @@ -63,10 +63,30 @@ public class LayoutManager { this.connection = connection; } + /** + * Gets the layout manager associated with the given + * {@link ApplicationConnection}. + * + * @param connection + * the application connection to get a layout manager for + * @return the layout manager associated with the provided application + * connection + */ public static LayoutManager get(ApplicationConnection connection) { return connection.getLayoutManager(); } + /** + * Registers that a ManagedLayout is depending on the size of an Element. + * This causes this layout manager to measure the element in the beginning + * of every layout phase and call the appropriate layout method of the + * managed layout if the size of the element has changed. + * + * @param owner + * the ManagedLayout that depends on an element + * @param element + * the Element that should be measured + */ public void registerDependency(ManagedLayout owner, Element element) { MeasuredSize measuredSize = ensureMeasured(element); setNeedsLayout(owner); @@ -98,6 +118,16 @@ public class LayoutManager { } } + /** + * Assigns a measured size to an element. Method defined as protected to + * allow separate implementation for IE8 in which delete not always works. + * + * @param element + * the dom element to attach the measured size to + * @param measuredSize + * the measured size to attach to the element. If + * <code>null</code>, any previous measured size is removed. + */ protected native void setMeasuredSize(Element element, MeasuredSize measuredSize) /*-{ @@ -124,6 +154,17 @@ public class LayoutManager { return measuredSize; } + /** + * Registers that a ManagedLayout is no longer depending on the size of an + * Element. + * + * @see #registerDependency(ManagedLayout, Element) + * + * @param owner + * the ManagedLayout no longer depends on an element + * @param element + * the Element that that no longer needs to be measured + */ public void unregisterDependency(ManagedLayout owner, Element element) { MeasuredSize measuredSize = getMeasuredSize(element, null); if (measuredSize == null) { @@ -176,8 +217,6 @@ public class LayoutManager { private void doLayout() { VConsole.log("Starting layout phase"); - Duration totalDuration = new Duration(); - Map<ManagedLayout, Integer> layoutCounts = new HashMap<ManagedLayout, Integer>(); int passes = 0; @@ -192,9 +231,6 @@ public class LayoutManager { needsVerticalLayout.clear(); for (ComponentConnector connector : needsMeasure) { - // Ensure the MeasuredSize is attached to the DOM element before - // doing measurements to avoid reflows in "some" browsers - getMeasuredSize(connector); currentDependencyTree.setNeedsMeasure(connector, true); } needsMeasure.clear(); @@ -358,9 +394,8 @@ public class LayoutManager { VConsole.log("Invoke post layout listeners in " + (totalDuration.elapsedMillis() - postLayoutStart) + " ms"); - VConsole.log("<b>Total layout phase time: " - + totalDuration.elapsedMillis() + "ms</b>"); - + VConsole.log("Total layout phase time: " + + totalDuration.elapsedMillis() + "ms"); } private void logConnectorStatus(int connectorId) { @@ -573,84 +608,408 @@ public class LayoutManager { layoutNow(); } - // TODO Rename to setNeedsLayout + /** + * Marks that a ManagedLayout should be layouted in the next layout phase + * even if none of the elements managed by the layout have been resized. + * + * @param layout + * the managed layout that should be layouted + */ public final void setNeedsLayout(ManagedLayout layout) { setNeedsHorizontalLayout(layout); setNeedsVerticalLayout(layout); } + /** + * Marks that a ManagedLayout should be layouted horizontally in the next + * layout phase even if none of the elements managed by the layout have been + * resized horizontally. + * + * For SimpleManagedLayout which is always layouted in both directions, this + * has the same effect as {@link #setNeedsLayout(ManagedLayout)}. + * + * @param layout + * the managed layout that should be layouted + */ public final void setNeedsHorizontalLayout(ManagedLayout layout) { needsHorizontalLayout.add(layout); } + /** + * Marks that a ManagedLayout should be layouted vertically in the next + * layout phase even if none of the elements managed by the layout have been + * resized vertically. + * + * For SimpleManagedLayout which is always layouted in both directions, this + * has the same effect as {@link #setNeedsLayout(ManagedLayout)}. + * + * @param layout + * the managed layout that should be layouted + */ public final void setNeedsVerticalLayout(ManagedLayout layout) { needsVerticalLayout.add(layout); } - public boolean isMeasured(Element element) { - return getMeasuredSize(element, nullSize) != nullSize; - } - + /** + * Gets the outer height (including margins, paddings and borders) of the + * given element, provided that it has been measured. These elements are + * guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * -1 is returned if the element has not been measured. If 0 is returned, it + * might indicate that the element is not attached to the DOM. + * + * @param element + * the element to get the measured size for + * @return the measured outer height (including margins, paddings and + * borders) of the element in pixels. + */ public final int getOuterHeight(Element element) { return getMeasuredSize(element, nullSize).getOuterHeight(); } + /** + * Gets the outer width (including margins, paddings and borders) of the + * given element, provided that it has been measured. These elements are + * guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * -1 is returned if the element has not been measured. If 0 is returned, it + * might indicate that the element is not attached to the DOM. + * + * @param element + * the element to get the measured size for + * @return the measured outer width (including margins, paddings and + * borders) of the element in pixels. + */ public final int getOuterWidth(Element element) { return getMeasuredSize(element, nullSize).getOuterWidth(); } + /** + * Gets the inner height (excluding margins, paddings and borders) of the + * given element, provided that it has been measured. These elements are + * guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * -1 is returned if the element has not been measured. If 0 is returned, it + * might indicate that the element is not attached to the DOM. + * + * @param element + * the element to get the measured size for + * @return the measured inner height (excluding margins, paddings and + * borders) of the element in pixels. + */ public final int getInnerHeight(Element element) { return getMeasuredSize(element, nullSize).getInnerHeight(); } + /** + * Gets the inner width (excluding margins, paddings and borders) of the + * given element, provided that it has been measured. These elements are + * guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * -1 is returned if the element has not been measured. If 0 is returned, it + * might indicate that the element is not attached to the DOM. + * + * @param element + * the element to get the measured size for + * @return the measured inner width (excluding margins, paddings and + * borders) of the element in pixels. + */ public final int getInnerWidth(Element element) { return getMeasuredSize(element, nullSize).getInnerWidth(); } + /** + * Gets the border height (top border + bottom border) of the given element, + * provided that it has been measured. These elements are guaranteed to be + * measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured border height (top border + bottom border) of the + * element in pixels. + */ public final int getBorderHeight(Element element) { return getMeasuredSize(element, nullSize).getBorderHeight(); } + /** + * Gets the padding height (top padding + bottom padding) of the given + * element, provided that it has been measured. These elements are + * guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured padding height (top padding + bottom padding) of the + * element in pixels. + */ public int getPaddingHeight(Element element) { return getMeasuredSize(element, nullSize).getPaddingHeight(); } + /** + * Gets the border width (left border + right border) of the given element, + * provided that it has been measured. These elements are guaranteed to be + * measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured border width (left border + right border) of the + * element in pixels. + */ public int getBorderWidth(Element element) { return getMeasuredSize(element, nullSize).getBorderWidth(); } + /** + * Gets the padding width (left padding + right padding) of the given + * element, provided that it has been measured. These elements are + * guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured padding width (left padding + right padding) of the + * element in pixels. + */ public int getPaddingWidth(Element element) { return getMeasuredSize(element, nullSize).getPaddingWidth(); } + /** + * Gets the top padding of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured top padding of the element in pixels. + */ public int getPaddingTop(Element element) { return getMeasuredSize(element, nullSize).getPaddingTop(); } + /** + * Gets the left padding of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured left padding of the element in pixels. + */ public int getPaddingLeft(Element element) { return getMeasuredSize(element, nullSize).getPaddingLeft(); } + /** + * Gets the bottom padding of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured bottom padding of the element in pixels. + */ public int getPaddingBottom(Element element) { return getMeasuredSize(element, nullSize).getPaddingBottom(); } + /** + * Gets the right padding of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured right padding of the element in pixels. + */ public int getPaddingRight(Element element) { return getMeasuredSize(element, nullSize).getPaddingRight(); } + /** + * Gets the top margin of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured top margin of the element in pixels. + */ public int getMarginTop(Element element) { return getMeasuredSize(element, nullSize).getMarginTop(); } + /** + * Gets the right margin of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured right margin of the element in pixels. + */ public int getMarginRight(Element element) { return getMeasuredSize(element, nullSize).getMarginRight(); } + /** + * Gets the bottom margin of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured bottom margin of the element in pixels. + */ public int getMarginBottom(Element element) { return getMeasuredSize(element, nullSize).getMarginBottom(); } + /** + * Gets the left margin of the given element, provided that it has been + * measured. These elements are guaranteed to be measured: + * <ul> + * <li>ManagedLayotus and their child Connectors + * <li>Elements for which there is at least one ElementResizeListener + * <li>Elements for which at least one ManagedLayout has registered a + * dependency + * </ul> + * + * A negative number is returned if the element has not been measured. If 0 + * is returned, it might indicate that the element is not attached to the + * DOM. + * + * @param element + * the element to get the measured size for + * @return the measured left margin of the element in pixels. + */ public int getMarginLeft(Element element) { return getMeasuredSize(element, nullSize).getMarginLeft(); } @@ -663,6 +1022,19 @@ public class LayoutManager { return getMeasuredSize(element, nullSize).getMarginHeight(); } + /** + * Registers the outer height (including margins, borders and paddings) of a + * component. This can be used as an optimization by ManagedLayouts; by + * informing the LayoutManager about what size a component will have, the + * layout propagation can continue directly without first measuring the + * potentially resized elements. + * + * @param component + * the component for which the size is reported + * @param outerHeight + * the new outer height (including margins, borders and paddings) + * of the component in pixels + */ public void reportOuterHeight(ComponentConnector component, int outerHeight) { MeasuredSize measuredSize = getMeasuredSize(component); if (isLayoutRunning()) { @@ -679,6 +1051,19 @@ public class LayoutManager { } } + /** + * Registers the height reserved for a relatively sized component. This can + * be used as an optimization by ManagedLayouts; by informing the + * LayoutManager about what size a component will have, the layout + * propagation can continue directly without first measuring the potentially + * resized elements. + * + * @param component + * the relatively sized component for which the size is reported + * @param assignedHeight + * the inner height of the relatively sized component's parent + * element in pixels + */ public void reportHeightAssignedToRelative(ComponentConnector component, int assignedHeight) { assert component.isRelativeHeight(); @@ -689,6 +1074,19 @@ public class LayoutManager { reportOuterHeight(component, effectiveHeight); } + /** + * Registers the width reserved for a relatively sized component. This can + * be used as an optimization by ManagedLayouts; by informing the + * LayoutManager about what size a component will have, the layout + * propagation can continue directly without first measuring the potentially + * resized elements. + * + * @param component + * the relatively sized component for which the size is reported + * @param assignedWidth + * the inner width of the relatively sized component's parent + * element in pixels + */ public void reportWidthAssignedToRelative(ComponentConnector component, int assignedWidth) { assert component.isRelativeWidth(); @@ -703,6 +1101,19 @@ public class LayoutManager { return Float.parseFloat(size.substring(0, size.length() - 1)); } + /** + * Registers the outer width (including margins, borders and paddings) of a + * component. This can be used as an optimization by ManagedLayouts; by + * informing the LayoutManager about what size a component will have, the + * layout propagation can continue directly without first measuring the + * potentially resized elements. + * + * @param component + * the component for which the size is reported + * @param outerWidth + * the new outer width (including margins, borders and paddings) + * of the component in pixels + */ public void reportOuterWidth(ComponentConnector component, int outerWidth) { MeasuredSize measuredSize = getMeasuredSize(component); if (isLayoutRunning()) { @@ -719,6 +1130,18 @@ public class LayoutManager { } } + /** + * Adds a listener that will be notified whenever the size of a specific + * element changes. Adding a listener to an element also ensures that all + * sizes for that element will be available starting from the next layout + * phase. + * + * @param element + * the element that should be checked for size changes + * @param listener + * an ElementResizeListener that will be informed whenever the + * size of the target element has changed + */ public void addElementResizeListener(Element element, ElementResizeListener listener) { Collection<ElementResizeListener> listeners = elementResizeListeners @@ -731,6 +1154,18 @@ public class LayoutManager { listeners.add(listener); } + /** + * Removes an element resize listener from the provided element. This might + * cause this LayoutManager to stop tracking the size of the element if no + * other sources are interested in the size. + * + * @param element + * the element to which the element resize listener was + * previously added + * @param listener + * the ElementResizeListener that should no longer get informed + * about size changes to the target element. + */ public void removeElementResizeListener(Element element, ElementResizeListener listener) { Collection<ElementResizeListener> listeners = elementResizeListeners @@ -751,6 +1186,16 @@ public class LayoutManager { } } + /** + * Informs this LayoutManager that the size of a component might have + * changed. If there is no upcoming layout phase, a new layout phase is + * scheduled. This method should be used whenever a size might have changed + * from outside of Vaadin's normal update phase, e.g. when an icon has been + * loaded or when the user resizes some part of the UI using the mouse. + * + * @param component + * the component whose size might have changed. + */ public void setNeedsMeasure(ComponentConnector component) { if (isLayoutRunning()) { currentDependencyTree.setNeedsMeasure(component, true); diff --git a/src/com/vaadin/terminal/gwt/client/ServerConnector.java b/src/com/vaadin/terminal/gwt/client/ServerConnector.java index f179b29054..c10f4bb411 100644 --- a/src/com/vaadin/terminal/gwt/client/ServerConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ServerConnector.java @@ -46,11 +46,9 @@ public interface ServerConnector extends Connector { public ApplicationConnection getConnection(); /** - * Tests whether the connector is enabled or not. Disabled connectors will - * ignore all attempts at communications. Received messages will be - * discarded. This method must check that the connector is enabled in - * context, that is if it's parent is disabled, this method must return - * false. + * Tests whether the connector is enabled or not. This method checks that + * the connector is enabled in context, i.e. if the parent connector is + * disabled, this method must return false. * * @return true if the connector is enabled, false otherwise */ diff --git a/src/com/vaadin/terminal/gwt/client/Util.java b/src/com/vaadin/terminal/gwt/client/Util.java index bfe63caefd..c392a0ba9c 100644 --- a/src/com/vaadin/terminal/gwt/client/Util.java +++ b/src/com/vaadin/terminal/gwt/client/Util.java @@ -978,6 +978,19 @@ public class Util { */ final Element target = getElementFromPoint(touch.getClientX(), touch.getClientY()); + + /* + * Fixes infocusable form fields in Safari of iOS 5.x and some Android + * browsers. + */ + Widget targetWidget = findWidget(target, null); + if (targetWidget instanceof com.google.gwt.user.client.ui.Focusable) { + final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) targetWidget; + toBeFocusedWidget.setFocus(true); + } else if (targetWidget instanceof Focusable) { + ((Focusable) targetWidget).focus(); + } + Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { try { diff --git a/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java b/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java index 89e106f063..6e0417149c 100644 --- a/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java +++ b/src/com/vaadin/terminal/gwt/client/VBrowserDetails.java @@ -31,14 +31,19 @@ public class VBrowserDetails implements Serializable { private boolean isOpera = false; private boolean isIE = false; - private boolean isWindows = false; - private boolean isMacOSX = false; - private boolean isLinux = false; + private OperatingSystem os = OperatingSystem.UNKNOWN; + + public enum OperatingSystem { + UNKNOWN, WINDOWS, MACOSX, LINUX, IOS, ANDROID; + } private float browserEngineVersion = -1; private int browserMajorVersion = -1; private int browserMinorVersion = -1; + private int osMajorVersion = -1; + private int osMinorVersion = -1; + /** * Create an instance based on the given user agent. * @@ -122,14 +127,80 @@ public class VBrowserDetails implements Serializable { // Operating system if (userAgent.contains("windows ")) { - isWindows = true; + os = OperatingSystem.WINDOWS; } else if (userAgent.contains("linux")) { - isLinux = true; + if (userAgent.contains("android")) { + os = OperatingSystem.ANDROID; + parseAndroidVersion(userAgent); + } else { + os = OperatingSystem.LINUX; + + } } else if (userAgent.contains("macintosh") || userAgent.contains("mac osx") || userAgent.contains("mac os x")) { - isMacOSX = true; + if (userAgent.contains("ipad") || userAgent.contains("ipod") + || userAgent.contains("iphone")) { + os = OperatingSystem.IOS; + parseIOSVersion(userAgent); + } else { + os = OperatingSystem.MACOSX; + } + } + } + + private void parseAndroidVersion(String userAgent) { + // Android 5.1; + if (!userAgent.contains("android")) { + return; + } + + String osVersionString = safeSubstring(userAgent, + userAgent.indexOf("android ") + "android ".length(), + userAgent.length()); + osVersionString = safeSubstring(osVersionString, 0, + osVersionString.indexOf(";")); + String[] parts = osVersionString.split("\\."); + parseOsVersion(parts); + } + + private void parseIOSVersion(String userAgent) { + // OS 5_1 like Mac OS X + if (!userAgent.contains("os ") || !userAgent.contains(" like mac")) { + return; + } + + String osVersionString = safeSubstring(userAgent, + userAgent.indexOf("os ") + 3, userAgent.indexOf(" like mac")); + String[] parts = osVersionString.split("_"); + parseOsVersion(parts); + } + + private void parseOsVersion(String[] parts) { + osMajorVersion = -1; + osMinorVersion = -1; + + if (parts.length >= 1) { + try { + osMajorVersion = Integer.parseInt(parts[0]); + } catch (Exception e) { + } + } + if (parts.length >= 2) { + try { + osMinorVersion = Integer.parseInt(parts[1]); + } catch (Exception e) { + } + // Some Androids report version numbers as "2.1-update1" + if (osMinorVersion == -1 && parts[1].contains("-")) { + try { + osMinorVersion = Integer.parseInt(parts[1].substring(0, + parts[1].indexOf('-'))); + } catch (Exception ee) { + } + } } + } private void parseVersionString(String versionString) { @@ -306,7 +377,7 @@ public class VBrowserDetails implements Serializable { * @return true if run on Windows, false otherwise */ public boolean isWindows() { - return isWindows; + return os == OperatingSystem.WINDOWS; } /** @@ -315,7 +386,7 @@ public class VBrowserDetails implements Serializable { * @return true if run on Mac OSX, false otherwise */ public boolean isMacOSX() { - return isMacOSX; + return os == OperatingSystem.MACOSX; } /** @@ -324,7 +395,45 @@ public class VBrowserDetails implements Serializable { * @return true if run on Linux, false otherwise */ public boolean isLinux() { - return isLinux; + return os == OperatingSystem.LINUX; + } + + /** + * Tests if the browser is run on Android. + * + * @return true if run on Android, false otherwise + */ + public boolean isAndroid() { + return os == OperatingSystem.ANDROID; + } + + /** + * Tests if the browser is run in iOS. + * + * @return true if run in iOS, false otherwise + */ + public boolean isIOS() { + return os == OperatingSystem.IOS; + } + + /** + * Returns the major version of the operating system. Currently only + * supported for mobile devices (iOS/Android) + * + * @return The major version or -1 if unknown + */ + public int getOperatingSystemMajorVersion() { + return osMajorVersion; + } + + /** + * Returns the minor version of the operating system. Currently only + * supported for mobile devices (iOS/Android) + * + * @return The minor version or -1 if unknown + */ + public int getOperatingSystemMinorVersion() { + return osMinorVersion; } /** diff --git a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java index c2fa4f46bf..5eaf78f255 100644 --- a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java +++ b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java @@ -180,6 +180,9 @@ public class VDebugConsole extends VOverlay implements Console { private static final String help = "Drag title=move, shift-drag=resize, doubleclick title=min/max." + "Use debug=quiet to log only to browser console."; + private static final int DEFAULT_WIDTH = 650; + private static final int DEFAULT_HEIGHT = 400; + public VDebugConsole() { super(false, false); getElement().getStyle().setOverflow(Overflow.HIDDEN); @@ -301,10 +304,20 @@ public class VDebugConsole extends VOverlay implements Console { height = Integer.parseInt(split[3]); autoScrollValue = Boolean.valueOf(split[4]); } else { - width = 400; - height = 150; - top = Window.getClientHeight() - 160; - left = Window.getClientWidth() - 410; + int windowHeight = Window.getClientHeight(); + int windowWidth = Window.getClientWidth(); + width = DEFAULT_WIDTH; + height = DEFAULT_HEIGHT; + + if (height > windowHeight / 2) { + height = windowHeight / 2; + } + if (width > windowWidth / 2) { + width = windowWidth / 2; + } + + top = windowHeight - (height + 10); + left = windowWidth - (width + 10); } setPixelSize(width, height); setPopupPosition(left, top); diff --git a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java index 294626e71a..fdc06b0e21 100644 --- a/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java +++ b/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java @@ -163,6 +163,15 @@ public class JsonEncoder { return outerArray; } + /** + * Returns the transport type for the given value. Only returns a transport + * type for internally handled values. + * + * @param value + * The value that should be transported + * @return One of the JsonEncode.VTYPE_ constants or null if the value + * cannot be transported using an internally handled type. + */ private static String getTransportType(Object value) { if (value == null) { return VTYPE_NULL; diff --git a/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java index f4305ffcaa..e61775a640 100644 --- a/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java +++ b/src/com/vaadin/terminal/gwt/client/communication/MethodInvocation.java @@ -4,6 +4,7 @@ package com.vaadin.terminal.gwt.client.communication; +import java.io.Serializable; import java.util.Arrays; /** @@ -12,19 +13,24 @@ import java.util.Arrays; * * @since 7.0 */ -public class MethodInvocation { +public class MethodInvocation implements Serializable { private final String connectorId; private final String interfaceName; private final String methodName; - private final Object[] parameters; + private Object[] parameters; public MethodInvocation(String connectorId, String interfaceName, - String methodName, Object[] parameters) { + String methodName) { this.connectorId = connectorId; this.interfaceName = interfaceName; this.methodName = methodName; - this.parameters = parameters; + } + + public MethodInvocation(String connectorId, String interfaceName, + String methodName, Object[] parameters) { + this(connectorId, interfaceName, methodName); + setParameters(parameters); } public String getConnectorId() { @@ -43,9 +49,14 @@ public class MethodInvocation { return parameters; } + public void setParameters(Object[] parameters) { + this.parameters = parameters; + } + @Override public String toString() { return connectorId + ":" + interfaceName + "." + methodName + "(" + Arrays.toString(parameters) + ")"; } + }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java index 2458a27eac..fdb04f0ddf 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java @@ -5,8 +5,8 @@ package com.vaadin.terminal.gwt.client.ui; import java.util.Set; -import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.Focusable; +import com.google.gwt.user.client.ui.HasEnabled; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.ComponentConnector; @@ -102,10 +102,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector .getTabIndex()); } - if (getWidget() instanceof FocusWidget) { - FocusWidget fw = (FocusWidget) getWidget(); - fw.setEnabled(isEnabled()); - } + setWidgetEnabled(isEnabled()); // Style names String styleName = getStyleNames(getWidget().getStylePrimaryName()); @@ -141,6 +138,12 @@ public abstract class AbstractComponentConnector extends AbstractConnector updateComponentSize(); } + public void setWidgetEnabled(boolean widgetEnabled) { + if (getWidget() instanceof HasEnabled) { + ((HasEnabled) getWidget()).setEnabled(widgetEnabled); + } + } + private void updateComponentSize() { String newWidth = getState().getWidth(); String newHeight = getState().getHeight(); @@ -338,12 +341,13 @@ public abstract class AbstractComponentConnector extends AbstractConnector public void onUnregister() { super.onUnregister(); - // Warn if widget is still attached to DOM. It should never be at this - // point. + // Show an error if widget is still attached to DOM. It should never be + // at this point. if (getWidget() != null && getWidget().isAttached()) { - VConsole.log("Widget for unregistered connector " + getWidget().removeFromParent(); + VConsole.error("Widget is still attached to the DOM after the connector (" + Util.getConnectorString(this) - + " is still attached to the DOM."); + + ") has been unregistered. Widget was removed."); } } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java index d6ffc0f091..526631e4b2 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentContainerConnector.java @@ -23,6 +23,14 @@ public abstract class AbstractComponentContainerConnector extends private final boolean debugLogging = false; /** + * Temporary storage for last enabled state to be able to see if it has + * changed. Can be removed once we are able to listen specifically for + * enabled changes in the state. Widget.isEnabled() cannot be used as all + * Widgets do not implement HasEnabled + */ + private boolean lastWidgetEnabledState = true; + + /** * Default constructor */ public AbstractComponentContainerConnector() { @@ -85,4 +93,18 @@ public abstract class AbstractComponentContainerConnector extends ConnectorHierarchyChangeEvent.TYPE, handler); } + @Override + public void setWidgetEnabled(boolean widgetEnabled) { + if (lastWidgetEnabledState == widgetEnabled) { + return; + } + lastWidgetEnabledState = widgetEnabled; + + super.setWidgetEnabled(widgetEnabled); + for (ComponentConnector c : getChildren()) { + // Update children as they might be affected by the enabled state of + // their parent + c.setWidgetEnabled(c.isEnabled()); + } + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/ClickRPC.java b/src/com/vaadin/terminal/gwt/client/ui/ClickRpc.java index 6c81b282e8..37d6443f55 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/ClickRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/ClickRpc.java @@ -6,7 +6,7 @@ package com.vaadin.terminal.gwt.client.ui; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -public interface ClickRPC extends ServerRpc { +public interface ClickRpc extends ServerRpc { /** * Called when a click event has occurred and there are server side * listeners for the event. diff --git a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java index 6b22f3c9f3..62697c4d98 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java @@ -3,6 +3,8 @@ */ package com.vaadin.terminal.gwt.client.ui; +import java.util.ArrayList; + import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.DivElement; @@ -21,6 +23,7 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.impl.FocusImpl; +import com.vaadin.terminal.gwt.client.BrowserInfo; /** * A scrollhandlers similar to {@link ScrollPanel}. @@ -126,7 +129,11 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements * @return the vertical scroll position, in pixels */ public int getScrollPosition() { - return getElement().getScrollTop(); + if (getElement().getPropertyJSO("_vScrollTop") != null) { + return getElement().getPropertyInt("_vScrollTop"); + } else { + return getElement().getScrollTop(); + } } /** @@ -146,7 +153,18 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements * the new vertical scroll position, in pixels */ public void setScrollPosition(int position) { - getElement().setScrollTop(position); + if (BrowserInfo.get().isAndroidWithBrokenScrollTop()) { + ArrayList<com.google.gwt.dom.client.Element> elements = TouchScrollDelegate + .getElements(getElement()); + for (com.google.gwt.dom.client.Element el : elements) { + final Style style = el.getStyle(); + style.setProperty("webkitTransform", "translate3d(0px," + + -position + "px,0px)"); + } + getElement().setPropertyInt("_vScrollTop", position); + } else { + getElement().setScrollTop(position); + } } public void onScroll(ScrollEvent event) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java index 09caa9ad82..7a5d85e34b 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java +++ b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickEventHandler.java @@ -35,5 +35,5 @@ public abstract class LayoutClickEventHandler extends AbstractClickEventHandler getLayoutClickRPC().layoutClick(mouseDetails, getChildComponent(event)); } - protected abstract LayoutClickRPC getLayoutClickRPC(); + protected abstract LayoutClickRpc getLayoutClickRPC(); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/LayoutClickRPC.java b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickRpc.java index 20b9c8750b..5b76f398a9 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/LayoutClickRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/LayoutClickRpc.java @@ -7,7 +7,7 @@ import com.vaadin.terminal.gwt.client.Connector; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -public interface LayoutClickRPC extends ServerRpc { +public interface LayoutClickRpc extends ServerRpc { /** * Called when a layout click event has occurred and there are server side * listeners for the event. diff --git a/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java b/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java index 3c08741de5..8b2248aff6 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java +++ b/src/com/vaadin/terminal/gwt/client/ui/TouchScrollDelegate.java @@ -4,19 +4,22 @@ package com.vaadin.terminal.gwt.client.ui; import java.util.ArrayList; -import java.util.Date; +import com.google.gwt.animation.client.Animation; +import com.google.gwt.core.client.Duration; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Touch; +import com.google.gwt.event.dom.client.ScrollHandler; import com.google.gwt.event.dom.client.TouchStartEvent; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; +import com.vaadin.terminal.gwt.client.BrowserInfo; import com.vaadin.terminal.gwt.client.VConsole; /** @@ -64,27 +67,33 @@ public class TouchScrollDelegate implements NativePreviewHandler { private static final double FRICTION = 0.002; private static final double DECELERATION = 0.002; private static final int MAX_DURATION = 1500; - private int origX; private int origY; private Element[] scrollableElements; private Element scrolledElement; private int origScrollTop; private HandlerRegistration handlerRegistration; + private double lastAnimatedTranslateY; private int lastClientY; - private double pixxelsPerMs; - private boolean transitionPending = false; private int deltaScrollPos; private boolean transitionOn = false; private int finalScrollTop; private ArrayList<Element> layers; private boolean moved; + private ScrollHandler scrollHandler; private static TouchScrollDelegate activeScrollDelegate; + private static final boolean androidWithBrokenScrollTop = BrowserInfo.get() + .isAndroidWithBrokenScrollTop(); + public TouchScrollDelegate(Element... elements) { scrollableElements = elements; } + public void setScrollHandler(ScrollHandler scrollHandler) { + this.scrollHandler = scrollHandler; + } + public static TouchScrollDelegate getActiveScrollDelegate() { return activeScrollDelegate; } @@ -116,37 +125,8 @@ public class TouchScrollDelegate implements NativePreviewHandler { public void onTouchStart(TouchStartEvent event) { if (activeScrollDelegate == null && event.getTouches().length() == 1) { - - Touch touch = event.getTouches().get(0); - if (detectScrolledElement(touch)) { - VConsole.log("TouchDelegate takes over"); - event.stopPropagation(); - handlerRegistration = Event.addNativePreviewHandler(this); - activeScrollDelegate = this; - hookTransitionEndListener(scrolledElement - .getFirstChildElement()); - origX = touch.getClientX(); - origY = touch.getClientY(); - yPositions[0] = origY; - eventTimeStamps[0] = new Date(); - nextEvent = 1; - - if (transitionOn) { - // TODO calculate current position of ongoing transition, - // fix to that and start scroll from there. Another option - // is to investigate if we can get even close the same - // framerate with scheduler based impl instead of using - // transitions (GWT examples has impl of this, with jsni - // though). This is very smooth on native ipad, now we - // ignore touch starts during animation. - origScrollTop = scrolledElement.getScrollTop(); - } else { - origScrollTop = scrolledElement.getScrollTop(); - } - moved = false; - // event.preventDefault(); - // event.stopPropagation(); - } + NativeEvent nativeEvent = event.getNativeEvent(); + doTouchStart(nativeEvent); } else { /* * Touch scroll is currenly on (possibly bouncing). Ignore. @@ -154,16 +134,39 @@ public class TouchScrollDelegate implements NativePreviewHandler { } } - private native void hookTransitionEndListener(Element element) - /*-{ - if(!element.hasTransitionEndListener) { - var that = this; - element.addEventListener("webkitTransitionEnd",function(event){ - that.@com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate::onTransitionEnd()(); - },false); - element.hasTransitionEndListener = true; + private void doTouchStart(NativeEvent nativeEvent) { + if (transitionOn) { + momentum.cancel(); + } + Touch touch = nativeEvent.getTouches().get(0); + if (detectScrolledElement(touch)) { + VConsole.log("TouchDelegate takes over"); + nativeEvent.stopPropagation(); + handlerRegistration = Event.addNativePreviewHandler(this); + activeScrollDelegate = this; + origY = touch.getClientY(); + yPositions[0] = origY; + eventTimeStamps[0] = getTimeStamp(); + nextEvent = 1; + + origScrollTop = getScrollTop(); + VConsole.log("ST" + origScrollTop); + + moved = false; + // event.preventDefault(); + // event.stopPropagation(); + } + } + + private int getScrollTop() { + if (androidWithBrokenScrollTop) { + if (scrolledElement.getPropertyJSO("_vScrollTop") != null) { + return scrolledElement.getPropertyInt("_vScrollTop"); + } + return 0; } - }-*/; + return scrolledElement.getScrollTop(); + } private void onTransitionEnd() { if (finalScrollTop < 0) { @@ -175,7 +178,6 @@ public class TouchScrollDelegate implements NativePreviewHandler { } else { moveTransformationToScrolloffset(); } - transitionOn = false; } private void animateToScrollPosition(int to, int from) { @@ -184,7 +186,14 @@ public class TouchScrollDelegate implements NativePreviewHandler { if (time <= 0) { time = 1; // get animation and transition end event } - translateTo(time, -to + origScrollTop); + VConsole.log("Animate " + time + " " + from + " " + to); + int translateTo = -to + origScrollTop; + int fromY = -from + origScrollTop; + if (androidWithBrokenScrollTop) { + fromY -= origScrollTop; + translateTo -= origScrollTop; + } + translateTo(time, fromY, translateTo); } private int getAnimationTimeForDistance(int dist) { @@ -200,16 +209,21 @@ public class TouchScrollDelegate implements NativePreviewHandler { * scrolltop, causing onscroll event. */ private void moveTransformationToScrolloffset() { - for (Element el : layers) { - Style style = el.getStyle(); - style.setProperty("webkitTransitionProperty", "none"); - style.setProperty("webkitTransform", "translate3d(0,0,0)"); + if (androidWithBrokenScrollTop) { + scrolledElement.setPropertyInt("_vScrollTop", finalScrollTop); + if (scrollHandler != null) { + scrollHandler.onScroll(null); + } + } else { + for (Element el : layers) { + Style style = el.getStyle(); + style.setProperty("webkitTransform", "translate3d(0,0,0)"); + } + scrolledElement.setScrollTop(finalScrollTop); } - scrolledElement.setScrollTop(finalScrollTop); activeScrollDelegate = null; handlerRegistration.removeHandler(); handlerRegistration = null; - } /** @@ -225,14 +239,7 @@ public class TouchScrollDelegate implements NativePreviewHandler { if (el.isOrHasChild(target) && el.getScrollHeight() > el.getClientHeight()) { scrolledElement = el; - NodeList<Node> childNodes = scrolledElement.getChildNodes(); - layers = new ArrayList<Element>(); - for (int i = 0; i < childNodes.getLength(); i++) { - Node item = childNodes.getItem(i); - if (item.getNodeType() == Node.ELEMENT_NODE) { - layers.add((Element) item); - } - } + layers = getElements(scrolledElement); return true; } @@ -240,10 +247,21 @@ public class TouchScrollDelegate implements NativePreviewHandler { return false; } + public static ArrayList<Element> getElements(Element scrolledElement2) { + NodeList<Node> childNodes = scrolledElement2.getChildNodes(); + ArrayList<Element> l = new ArrayList<Element>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node item = childNodes.getItem(i); + if (item.getNodeType() == Node.ELEMENT_NODE) { + l.add((Element) item); + } + } + return l; + } + private void onTouchMove(NativeEvent event) { if (!moved) { - Date date = new Date(); - long l = (date.getTime() - eventTimeStamps[0].getTime()); + double l = (getTimeStamp() - eventTimeStamps[0]); VConsole.log(l + " ms from start to move"); } boolean handleMove = readPositionAndSpeed(event); @@ -255,15 +273,15 @@ public class TouchScrollDelegate implements NativePreviewHandler { int overscroll = (deltaScrollTop + origScrollTop) - getMaxFinalY(); overscroll = overscroll / 2; - if (overscroll > scrolledElement.getClientHeight() / 2) { - overscroll = scrolledElement.getClientHeight() / 2; + if (overscroll > getMaxOverScroll()) { + overscroll = getMaxOverScroll(); } deltaScrollTop = getMaxFinalY() + overscroll - origScrollTop; } else if (finalPos < 0) { // spring effect at the beginning int overscroll = finalPos / 2; - if (-overscroll > scrolledElement.getClientHeight() / 2) { - overscroll = -scrolledElement.getClientHeight() / 2; + if (-overscroll > getMaxOverScroll()) { + overscroll = -getMaxOverScroll(); } deltaScrollTop = overscroll - origScrollTop; } @@ -276,16 +294,20 @@ public class TouchScrollDelegate implements NativePreviewHandler { private void quickSetScrollPosition(int deltaX, int deltaY) { deltaScrollPos = deltaY; - translateTo(0, -deltaScrollPos); + if (androidWithBrokenScrollTop) { + deltaY += origScrollTop; + translateTo(-deltaY); + } else { + translateTo(-deltaScrollPos); + } } private static final int EVENTS_FOR_SPEED_CALC = 3; public static final int SIGNIFICANT_MOVE_THRESHOLD = 3; private int[] yPositions = new int[EVENTS_FOR_SPEED_CALC]; - private Date[] eventTimeStamps = new Date[EVENTS_FOR_SPEED_CALC]; + private double[] eventTimeStamps = new double[EVENTS_FOR_SPEED_CALC]; private int nextEvent = 0; - private Date transitionStart; - private Date transitionDuration; + private Animation momentum; /** * @@ -293,12 +315,11 @@ public class TouchScrollDelegate implements NativePreviewHandler { * @return */ private boolean readPositionAndSpeed(NativeEvent event) { - Date now = new Date(); Touch touch = event.getChangedTouches().get(0); lastClientY = touch.getClientY(); int eventIndx = nextEvent++; eventIndx = eventIndx % EVENTS_FOR_SPEED_CALC; - eventTimeStamps[eventIndx] = now; + eventTimeStamps[eventIndx] = getTimeStamp(); yPositions[eventIndx] = lastClientY; return isMovedSignificantly(); } @@ -348,15 +369,11 @@ public class TouchScrollDelegate implements NativePreviewHandler { // VConsole.log("To max overscroll"); finalY = getMaxFinalY() + getMaxOverScroll(); int fixedPixelsToMove = finalY - currentY; - pixelsPerMs = pixelsPerMs * pixelsToMove / fixedPixelsToMove - / FRICTION; pixelsToMove = fixedPixelsToMove; } else if (finalY < 0 - getMaxOverScroll()) { // VConsole.log("to min overscroll"); finalY = -getMaxOverScroll(); int fixedPixelsToMove = finalY - currentY; - pixelsPerMs = pixelsPerMs * pixelsToMove / fixedPixelsToMove - / FRICTION; pixelsToMove = fixedPixelsToMove; } else { duration = (int) (Math.abs(pixelsPerMs / DECELERATION)); @@ -380,8 +397,13 @@ public class TouchScrollDelegate implements NativePreviewHandler { return; } - int translateY = -finalY + origScrollTop; - translateTo(duration, translateY); + int translateTo = -finalY + origScrollTop; + int fromY = -currentY + origScrollTop; + if (androidWithBrokenScrollTop) { + fromY -= origScrollTop; + translateTo -= origScrollTop; + } + translateTo(duration, fromY, translateTo); } private double calculateSpeed() { @@ -392,47 +414,75 @@ public class TouchScrollDelegate implements NativePreviewHandler { } int idx = nextEvent % EVENTS_FOR_SPEED_CALC; final int firstPos = yPositions[idx]; - final Date firstTs = eventTimeStamps[idx]; + final double firstTs = eventTimeStamps[idx]; idx += EVENTS_FOR_SPEED_CALC; idx--; idx = idx % EVENTS_FOR_SPEED_CALC; final int lastPos = yPositions[idx]; - final Date lastTs = eventTimeStamps[idx]; + final double lastTs = eventTimeStamps[idx]; // speed as in change of scrolltop == -speedOfTouchPos - return (firstPos - lastPos) - / (double) (lastTs.getTime() - firstTs.getTime()); + return (firstPos - lastPos) / (lastTs - firstTs); } /** * Note positive scrolltop moves layer up, positive translate moves layer * down. - * - * @param duration - * @param translateY */ - private void translateTo(int duration, int translateY) { + private void translateTo(double translateY) { for (Element el : layers) { - final Style style = el.getStyle(); - if (duration > 0) { - style.setProperty("webkitTransitionDuration", duration + "ms"); - style.setProperty("webkitTransitionTimingFunction", - "cubic-bezier(0,0,0.25,1)"); - style.setProperty("webkitTransitionProperty", - "-webkit-transform"); - transitionOn = true; - transitionStart = new Date(); - transitionDuration = new Date(); - } else { - style.setProperty("webkitTransitionProperty", "none"); - } + Style style = el.getStyle(); style.setProperty("webkitTransform", "translate3d(0px," + translateY + "px,0px)"); } } + /** + * Note positive scrolltop moves layer up, positive translate moves layer + * down. + * + * @param duration + */ + private void translateTo(int duration, final int fromY, final int finalY) { + if (duration > 0) { + transitionOn = true; + + momentum = new Animation() { + + @Override + protected void onUpdate(double progress) { + lastAnimatedTranslateY = (fromY + (finalY - fromY) + * progress); + translateTo(lastAnimatedTranslateY); + } + + @Override + protected double interpolate(double progress) { + return 1 + Math.pow(progress - 1, 3); + } + + @Override + protected void onComplete() { + super.onComplete(); + transitionOn = false; + onTransitionEnd(); + } + + @Override + protected void onCancel() { + int delta = (int) (finalY - lastAnimatedTranslateY); + finalScrollTop -= delta; + moveTransformationToScrolloffset(); + transitionOn = false; + } + }; + momentum.run(duration); + } + } + private int getMaxOverScroll() { - return scrolledElement.getClientHeight() / 4; + return androidWithBrokenScrollTop ? 0 : scrolledElement + .getClientHeight() / 3; } private int getMaxFinalY() { @@ -441,14 +491,18 @@ public class TouchScrollDelegate implements NativePreviewHandler { } public void onPreviewNativeEvent(NativePreviewEvent event) { + int typeInt = event.getTypeInt(); if (transitionOn) { /* * TODO allow starting new events. See issue in onTouchStart */ event.cancel(); + + if (typeInt == Event.ONTOUCHSTART) { + doTouchStart(event.getNativeEvent()); + } return; } - int typeInt = event.getTypeInt(); switch (typeInt) { case Event.ONTOUCHMOVE: if (!event.isCanceled()) { @@ -484,4 +538,15 @@ public class TouchScrollDelegate implements NativePreviewHandler { public void setElements(com.google.gwt.user.client.Element[] elements) { scrollableElements = elements; } + + /** + * long calcucation are not very efficient in GWT, so this helper method + * returns timestamp in double. + * + * @return + */ + public static double getTimeStamp() { + return Duration.currentTimeMillis(); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java index 2307f67ac5..80b6254e02 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java @@ -21,7 +21,7 @@ import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector; import com.vaadin.terminal.gwt.client.ui.Connect; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; import com.vaadin.terminal.gwt.client.ui.absolutelayout.VAbsoluteLayout.AbsoluteWrapper; import com.vaadin.ui.AbsoluteLayout; @@ -38,20 +38,20 @@ public class AbsoluteLayoutConnector extends } @Override - protected LayoutClickRPC getLayoutClickRPC() { + protected LayoutClickRpc getLayoutClickRPC() { return rpc; }; }; - private AbsoluteLayoutServerRPC rpc; + private AbsoluteLayoutServerRpc rpc; private Map<String, AbsoluteWrapper> connectorIdToComponentWrapper = new HashMap<String, AbsoluteWrapper>(); @Override protected void init() { super.init(); - rpc = RpcProxy.create(AbsoluteLayoutServerRPC.class, this); + rpc = RpcProxy.create(AbsoluteLayoutServerRpc.class, this); } /** diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRpc.java index df7ee1b82e..d626eb5b6c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutServerRpc.java @@ -4,8 +4,8 @@ package com.vaadin.terminal.gwt.client.ui.absolutelayout; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; -public interface AbsoluteLayoutServerRPC extends LayoutClickRPC, ServerRpc { +public interface AbsoluteLayoutServerRpc extends LayoutClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java index 62a5e8ac8b..a555ecd392 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java @@ -57,7 +57,11 @@ public class ButtonConnector extends AbstractComponentConnector implements blurHandlerRegistration = EventHelper.updateBlurHandler(this, blurHandlerRegistration); // Set text - getWidget().setText(getState().getCaption()); + if (getState().isHtmlContentAllowed()) { + getWidget().setHtml(getState().getCaption()); + } else { + getWidget().setText(getState().getCaption()); + } // handle error if (null != getState().getErrorMessage()) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java index f26cdae0c6..fdc053b3ae 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java +++ b/src/com/vaadin/terminal/gwt/client/ui/button/ButtonState.java @@ -17,6 +17,10 @@ import com.vaadin.ui.Button; public class ButtonState extends ComponentState { private boolean disableOnClick = false; private int clickShortcutKeyCode = 0; + /** + * If caption should be rendered in HTML + */ + private boolean htmlContentAllowed = false; /** * Checks whether the button should be disabled on the client side on next @@ -62,4 +66,30 @@ public class ButtonState extends ComponentState { this.clickShortcutKeyCode = clickShortcutKeyCode; } + /** + * Set whether the caption text is rendered as HTML or not. You might need + * to retheme button to allow higher content than the original text style. + * + * If set to true, the captions are passed to the browser as html and the + * developer is responsible for ensuring no harmful html is used. If set to + * false, the content is passed to the browser as plain text. + * + * @param htmlContentAllowed + * <code>true</code> if caption is rendered as HTML, + * <code>false</code> otherwise + */ + public void setHtmlContentAllowed(boolean htmlContentAllowed) { + this.htmlContentAllowed = htmlContentAllowed; + } + + /** + * Return HTML rendering setting. + * + * @return <code>true</code> if the caption text is to be rendered as HTML, + * <code>false</code> otherwise + */ + public boolean isHtmlContentAllowed() { + return htmlContentAllowed; + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java index f7d73d3b5e..e5e7dbba8b 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java +++ b/src/com/vaadin/terminal/gwt/client/ui/button/VButton.java @@ -98,6 +98,10 @@ public class VButton extends FocusWidget implements ClickHandler { captionElement.setInnerText(text); } + public void setHtml(String html) { + captionElement.setInnerHTML(html); + } + @SuppressWarnings("deprecation") @Override /* diff --git a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java index 76fb9ab926..7df31a8593 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutConnector.java @@ -20,7 +20,7 @@ import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; import com.vaadin.terminal.gwt.client.ui.AbstractLayoutConnector; import com.vaadin.terminal.gwt.client.ui.Connect; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; import com.vaadin.terminal.gwt.client.ui.VMarginInfo; import com.vaadin.terminal.gwt.client.ui.csslayout.VCssLayout.FlowPane; import com.vaadin.ui.CssLayout; @@ -38,19 +38,19 @@ public class CssLayoutConnector extends AbstractLayoutConnector { } @Override - protected LayoutClickRPC getLayoutClickRPC() { + protected LayoutClickRpc getLayoutClickRPC() { return rpc; }; }; - private CssLayoutServerRPC rpc; + private CssLayoutServerRpc rpc; private Map<ComponentConnector, VCaption> childToCaption = new HashMap<ComponentConnector, VCaption>(); @Override protected void init() { super.init(); - rpc = RpcProxy.create(CssLayoutServerRPC.class, this); + rpc = RpcProxy.create(CssLayoutServerRpc.class, this); } @Override diff --git a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRpc.java index 067ea54fdc..7ba89d4c4c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/csslayout/CssLayoutServerRpc.java @@ -4,8 +4,8 @@ package com.vaadin.terminal.gwt.client.ui.csslayout; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; -public interface CssLayoutServerRPC extends LayoutClickRPC, ServerRpc { +public interface CssLayoutServerRpc extends LayoutClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java b/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java index 5208d7cacf..b4194c40a6 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/customlayout/VCustomLayout.java @@ -362,9 +362,9 @@ public class VCustomLayout extends ComplexPanel { private native void publishResizedFunction(Element element) /*-{ var self = this; - element.notifyChildrenOfSizeChange = function() { + element.notifyChildrenOfSizeChange = $entry(function() { self.@com.vaadin.terminal.gwt.client.ui.customlayout.VCustomLayout::notifyChildrenOfSizeChange()(); - }; + }); }-*/; /** diff --git a/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateFieldCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateFieldCalendar.java index 21e0e0820d..84b3c678eb 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateFieldCalendar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/datefield/VDateFieldCalendar.java @@ -45,6 +45,13 @@ public class VDateFieldCalendar extends VDateField { */ @SuppressWarnings("deprecation") protected void updateValueFromPanel() { + + // If field is invisible at the beginning, client can still be null when + // this function is called. + if (getClient() == null) { + return; + } + Date date2 = calendarPanel.getDate(); Date currentDate = getCurrentDate(); if (currentDate == null || date2.getTime() != currentDate.getTime()) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java index 5cbfabbb11..d09b81e1e1 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java +++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java @@ -508,9 +508,9 @@ public class VDragAndDropWrapper extends VCustomComponent implements protected native void hookHtml5DragStart(Element el) /*-{ var me = this; - el.addEventListener("dragstart", function(ev) { + el.addEventListener("dragstart", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }, false); + }), false); }-*/; /** @@ -522,21 +522,21 @@ public class VDragAndDropWrapper extends VCustomComponent implements /*-{ var me = this; - el.addEventListener("dragenter", function(ev) { + el.addEventListener("dragenter", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }, false); + }), false); - el.addEventListener("dragleave", function(ev) { + el.addEventListener("dragleave", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }, false); + }), false); - el.addEventListener("dragover", function(ev) { + el.addEventListener("dragover", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }, false); + }), false); - el.addEventListener("drop", function(ev) { + el.addEventListener("drop", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }, false); + }), false); }-*/; public boolean updateDropDetails(VDragEvent drag) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java index f819b0559a..bb511524e5 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java +++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapperIE.java @@ -39,9 +39,9 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper { /*-{ var me = this; - el.attachEvent("ondragstart", function(ev) { + el.attachEvent("ondragstart", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragStart(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }); + })); }-*/; @Override @@ -49,21 +49,21 @@ public class VDragAndDropWrapperIE extends VDragAndDropWrapper { /*-{ var me = this; - el.attachEvent("ondragenter", function(ev) { + el.attachEvent("ondragenter", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragEnter(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }); + })); - el.attachEvent("ondragleave", function(ev) { + el.attachEvent("ondragleave", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragLeave(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }); + })); - el.attachEvent("ondragover", function(ev) { + el.attachEvent("ondragover", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragOver(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }); + })); - el.attachEvent("ondrop", function(ev) { + el.attachEvent("ondrop", $entry(function(ev) { return me.@com.vaadin.terminal.gwt.client.ui.draganddropwrapper.VDragAndDropWrapper::html5DragDrop(Lcom/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent;)(ev); - }); + })); }-*/; } diff --git a/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java index 423aac1974..81ac195c8e 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedConnector.java @@ -35,12 +35,12 @@ public class EmbeddedConnector extends AbstractComponentConnector implements public static final String ALTERNATE_TEXT = "alt"; - EmbeddedServerRPC rpc; + EmbeddedServerRpc rpc; @Override protected void init() { super.init(); - rpc = RpcProxy.create(EmbeddedServerRPC.class, this); + rpc = RpcProxy.create(EmbeddedServerRpc.class, this); } public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRpc.java index e3410e408e..7f36c812bc 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/embedded/EmbeddedServerRpc.java @@ -4,7 +4,7 @@ package com.vaadin.terminal.gwt.client.ui.embedded; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.ClickRPC; +import com.vaadin.terminal.gwt.client.ui.ClickRpc; -public interface EmbeddedServerRPC extends ClickRPC, ServerRpc { +public interface EmbeddedServerRpc extends ClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java index 82f26383a1..e4a31b96ef 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java @@ -22,7 +22,7 @@ import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector; import com.vaadin.terminal.gwt.client.ui.AlignmentInfo; import com.vaadin.terminal.gwt.client.ui.Connect; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; import com.vaadin.terminal.gwt.client.ui.VMarginInfo; import com.vaadin.terminal.gwt.client.ui.gridlayout.VGridLayout.Cell; import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot; @@ -41,18 +41,18 @@ public class GridLayoutConnector extends AbstractComponentContainerConnector } @Override - protected LayoutClickRPC getLayoutClickRPC() { + protected LayoutClickRpc getLayoutClickRPC() { return rpc; }; }; - private GridLayoutServerRPC rpc; + private GridLayoutServerRpc rpc; private boolean needCaptionUpdate = false; @Override public void init() { - rpc = RpcProxy.create(GridLayoutServerRPC.class, this); + rpc = RpcProxy.create(GridLayoutServerRpc.class, this); getLayoutManager().registerDependency(this, getWidget().spacingMeasureElement); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRpc.java index 1b7f1a15d9..cd8df297ec 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutServerRpc.java @@ -4,8 +4,8 @@ package com.vaadin.terminal.gwt.client.ui.gridlayout; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; -public interface GridLayoutServerRPC extends LayoutClickRPC, ServerRpc { +public interface GridLayoutServerRpc extends LayoutClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java index 801c405826..84d3b73285 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/NativeButtonConnector.java @@ -58,7 +58,11 @@ public class NativeButtonConnector extends AbstractComponentConnector implements blurHandlerRegistration); // Set text - getWidget().setText(getState().getCaption()); + if (getState().isHtmlContentAllowed()) { + getWidget().setHTML(getState().getCaption()); + } else { + getWidget().setText(getState().getCaption()); + } // handle error if (null != getState().getErrorMessage()) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java index d0b8f73eb1..01ac4fab6a 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java +++ b/src/com/vaadin/terminal/gwt/client/ui/nativebutton/VNativeButton.java @@ -65,6 +65,11 @@ public class VNativeButton extends Button implements ClickHandler { } @Override + public void setHTML(String html) { + captionElement.setInnerHTML(html); + } + + @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); diff --git a/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java b/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java index eb97160f52..0d222044ba 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java +++ b/src/com/vaadin/terminal/gwt/client/ui/notification/VNotification.java @@ -160,6 +160,13 @@ public class VNotification extends VOverlay { } super.show(); setPosition(position); + /** + * Android 4 fails to render notifications correctly without a little + * nudge (#8551) + */ + if (BrowserInfo.get().isAndroid()) { + Util.setStyleTemporarily(getElement(), "display", "none"); + } } @Override diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java index d36046d6cb..174da61bd3 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java @@ -23,7 +23,7 @@ import com.vaadin.terminal.gwt.client.communication.RpcProxy; import com.vaadin.terminal.gwt.client.ui.AbstractLayoutConnector; import com.vaadin.terminal.gwt.client.ui.AlignmentInfo; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; import com.vaadin.terminal.gwt.client.ui.VMarginInfo; import com.vaadin.terminal.gwt.client.ui.layout.ComponentConnectorLayoutSlot; import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot; @@ -31,7 +31,7 @@ import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot; public abstract class AbstractOrderedLayoutConnector extends AbstractLayoutConnector implements Paintable, DirectionalManagedLayout { - AbstractOrderedLayoutServerRPC rpc; + AbstractOrderedLayoutServerRpc rpc; private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler( this) { @@ -43,7 +43,7 @@ public abstract class AbstractOrderedLayoutConnector extends } @Override - protected LayoutClickRPC getLayoutClickRPC() { + protected LayoutClickRpc getLayoutClickRPC() { return rpc; }; @@ -51,7 +51,7 @@ public abstract class AbstractOrderedLayoutConnector extends @Override public void init() { - rpc = RpcProxy.create(AbstractOrderedLayoutServerRPC.class, this); + rpc = RpcProxy.create(AbstractOrderedLayoutServerRpc.class, this); getLayoutManager().registerDependency(this, getWidget().spacingMeasureElement); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRpc.java index f23cda3512..5a29eacada 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutServerRpc.java @@ -4,9 +4,9 @@ package com.vaadin.terminal.gwt.client.ui.orderedlayout; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.LayoutClickRPC; +import com.vaadin.terminal.gwt.client.ui.LayoutClickRpc; -public interface AbstractOrderedLayoutServerRPC extends LayoutClickRPC, +public interface AbstractOrderedLayoutServerRpc extends LayoutClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java index a8512762f1..9555c38a36 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java @@ -43,11 +43,11 @@ public class PanelConnector extends AbstractComponentContainerConnector private Integer uidlScrollLeft; - private PanelServerRPC rpc; + private PanelServerRpc rpc; @Override public void init() { - rpc = RpcProxy.create(PanelServerRPC.class, this); + rpc = RpcProxy.create(PanelServerRpc.class, this); VPanel panel = getWidget(); LayoutManager layoutManager = getLayoutManager(); diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRpc.java index 9a556cfe0e..9b59344aec 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelServerRpc.java @@ -4,8 +4,8 @@ package com.vaadin.terminal.gwt.client.ui.panel; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.ClickRPC; +import com.vaadin.terminal.gwt.client.ui.ClickRpc; -public interface PanelServerRPC extends ClickRPC, ServerRpc { +public interface PanelServerRpc extends ClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java index 408c15383c..46f82d60b7 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java @@ -47,7 +47,7 @@ import com.vaadin.ui.Root; public class RootConnector extends AbstractComponentContainerConnector implements Paintable, MayScrollChildren { - private RootServerRPC rpc = RpcProxy.create(RootServerRPC.class, this); + private RootServerRpc rpc = RpcProxy.create(RootServerRpc.class, this); private HandlerRegistration childStateChangeHandlerRegistration; diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/RootServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/root/RootServerRpc.java index f32b972dfe..389500949d 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/root/RootServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/root/RootServerRpc.java @@ -4,8 +4,8 @@ package com.vaadin.terminal.gwt.client.ui.root; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.ClickRPC; +import com.vaadin.terminal.gwt.client.ui.ClickRpc; -public interface RootServerRPC extends ClickRPC, ServerRpc { +public interface RootServerRpc extends ClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java index 13fc55d7ea..12a69d5556 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java +++ b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java @@ -306,6 +306,7 @@ public class VRoot extends SimplePanel implements ResizeHandler, /*-{ var j; for(j in $wnd.vaadin.vaadinConfigurations) { + // $entry not needed as function is not exported list.@java.util.Collection::add(Ljava/lang/Object;)(j); } }-*/; diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java index 7340c2cbd9..b3921204dc 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java @@ -30,12 +30,12 @@ import com.vaadin.terminal.gwt.client.ui.splitpanel.VAbstractSplitPanel.Splitter public abstract class AbstractSplitPanelConnector extends AbstractComponentContainerConnector implements SimpleManagedLayout { - private AbstractSplitPanelRPC rpc; + private AbstractSplitPanelRpc rpc; @Override protected void init() { super.init(); - rpc = RpcProxy.create(AbstractSplitPanelRPC.class, this); + rpc = RpcProxy.create(AbstractSplitPanelRpc.class, this); // TODO Remove getWidget().client = getConnection(); diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRPC.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRpc.java index 15cd47a656..cc043838ff 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelRpc.java @@ -6,7 +6,7 @@ package com.vaadin.terminal.gwt.client.ui.splitpanel; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -public interface AbstractSplitPanelRPC extends ServerRpc { +public interface AbstractSplitPanelRpc extends ServerRpc { /** * Called when the position has been updated by the user. diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java index 13f3adbc72..8b80eed840 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java +++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelState.java @@ -3,6 +3,8 @@ */ package com.vaadin.terminal.gwt.client.ui.splitpanel; +import java.io.Serializable; + import com.vaadin.terminal.gwt.client.ComponentState; import com.vaadin.terminal.gwt.client.Connector; @@ -44,7 +46,7 @@ public class AbstractSplitPanelState extends ComponentState { this.splitterState = splitterState; } - public static class SplitterState { + public static class SplitterState implements Serializable { private float position; private String positionUnit; private boolean positionReversed = false; diff --git a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java index 563ca04abe..c45c26c4ac 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java @@ -18,6 +18,7 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Display; @@ -545,6 +546,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (touchScrollDelegate == null) { touchScrollDelegate = new TouchScrollDelegate( scrollBodyPanel.getElement()); + touchScrollDelegate.setScrollHandler(this); } return touchScrollDelegate; @@ -3833,6 +3835,14 @@ public class VScrollTable extends FlowPanel implements HasWidgets, DOM.appendChild(container, preSpacer); DOM.appendChild(container, table); DOM.appendChild(container, postSpacer); + if (BrowserInfo.get().isTouchDevice()) { + NodeList<Node> childNodes = container.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Element item = (Element) childNodes.getItem(i); + item.getStyle().setProperty("webkitTransform", + "translate3d(0,0,0)"); + } + } } @@ -4370,7 +4380,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, public class VScrollTableRow extends Panel implements ActionOwner { - private static final int TOUCHSCROLL_TIMEOUT = 70; + private static final int TOUCHSCROLL_TIMEOUT = 100; private static final int DRAGMODE_MULTIROW = 2; protected ArrayList<Widget> childWidgets = new ArrayList<Widget>(); private boolean selected = false; @@ -5020,9 +5030,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } }; } - contextTouchTimeout.cancel(); - contextTouchTimeout - .schedule(TOUCH_CONTEXT_MENU_TIMEOUT); + if (contextTouchTimeout != null) { + contextTouchTimeout.cancel(); + contextTouchTimeout + .schedule(TOUCH_CONTEXT_MENU_TIMEOUT); + } } break; case Event.ONMOUSEDOWN: @@ -6175,6 +6187,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * The row to ensure is visible */ private void ensureRowIsVisible(VScrollTableRow row) { + if (BrowserInfo.get().isTouchDevice()) { + // Skip due to android devices that have broken scrolltop will may + // get odd scrolling here. + return; + } Util.scrollIntoViewVertically(row.getElement()); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java index b2141e06e5..7bd392b503 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/textfield/VTextField.java @@ -234,9 +234,9 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler, protected native void attachCutEventListener(Element el) /*-{ var me = this; - el.oncut = function() { + el.oncut = $entry(function() { me.@com.vaadin.terminal.gwt.client.ui.textfield.VTextField::onCut()(); - }; + }); }-*/; protected native void detachCutEventListener(Element el) diff --git a/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategy.java b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategy.java index 18cfc643d3..174a4b88ca 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategy.java +++ b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategy.java @@ -8,9 +8,9 @@ public class UploadIFrameOnloadStrategy { native void hookEvents(com.google.gwt.dom.client.Element iframe, VUpload upload) /*-{ - iframe.onload = function() { + iframe.onload = $entry(function() { upload.@com.vaadin.terminal.gwt.client.ui.upload.VUpload::onSubmitComplete()(); - }; + }); }-*/; /** diff --git a/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategyIE.java b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategyIE.java index 19d38a8a95..17a7e46bd5 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategyIE.java +++ b/src/com/vaadin/terminal/gwt/client/ui/upload/UploadIFrameOnloadStrategyIE.java @@ -13,11 +13,11 @@ public class UploadIFrameOnloadStrategyIE extends UploadIFrameOnloadStrategy { @Override native void hookEvents(Element iframe, VUpload upload) /*-{ - iframe.onreadystatechange = function() { + iframe.onreadystatechange = $entry(function() { if (iframe.readyState == 'complete') { upload.@com.vaadin.terminal.gwt.client.ui.upload.VUpload::onSubmitComplete()(); } - }; + }); }-*/; @Override diff --git a/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java b/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java index d40f954cdc..484000b8d1 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java +++ b/src/com/vaadin/terminal/gwt/client/ui/video/VVideo.java @@ -34,9 +34,9 @@ public class VVideo extends VMediaBase { private native void updateDimensionsWhenMetadataLoaded(Element el) /*-{ var self = this; - el.addEventListener('loadedmetadata', function(e) { - $entry(self.@com.vaadin.terminal.gwt.client.ui.video.VVideo::updateElementDynamicSize(II)(el.videoWidth, el.videoHeight)); - }, false); + el.addEventListener('loadedmetadata', $entry(function(e) { + self.@com.vaadin.terminal.gwt.client.ui.video.VVideo::updateElementDynamicSize(II)(el.videoWidth, el.videoHeight); + }), false); }-*/; diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java index 85f4213d3e..6979982a9c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java @@ -44,7 +44,7 @@ public class WindowConnector extends AbstractComponentContainerConnector } }; - private WindowServerRPC rpc; + private WindowServerRpc rpc; boolean minWidthChecked = false; @@ -56,7 +56,7 @@ public class WindowConnector extends AbstractComponentContainerConnector @Override protected void init() { super.init(); - rpc = RpcProxy.create(WindowServerRPC.class, this); + rpc = RpcProxy.create(WindowServerRpc.class, this); getLayoutManager().registerDependency(this, getWidget().contentPanel.getElement()); diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRPC.java b/src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRpc.java index 782284b562..4723c55786 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRPC.java +++ b/src/com/vaadin/terminal/gwt/client/ui/window/WindowServerRpc.java @@ -4,7 +4,7 @@ package com.vaadin.terminal.gwt.client.ui.window; import com.vaadin.terminal.gwt.client.communication.ServerRpc; -import com.vaadin.terminal.gwt.client.ui.ClickRPC; +import com.vaadin.terminal.gwt.client.ui.ClickRpc; -public interface WindowServerRPC extends ClickRPC, ServerRpc { +public interface WindowServerRpc extends ClickRpc, ServerRpc { }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java index 8235859758..77698805de 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java @@ -509,6 +509,9 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet protected void handleRequest(PortletRequest request, PortletResponse response) throws PortletException, IOException { + RequestTimer requestTimer = new RequestTimer(); + requestTimer.start(); + AbstractApplicationPortletWrapper portletWrapper = new AbstractApplicationPortletWrapper( this); @@ -610,6 +613,10 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet if (application.isRunning()) { switch (requestType) { case RENDER: + case ACTION: + // Both action requests and render requests are ok + // without a Root as they render the initial HTML + // and then do a second request try { root = application .getRootForRequest(wrappedRequest); @@ -714,6 +721,10 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet } finally { Root.setCurrentRoot(null); Application.setCurrentApplication(null); + + requestTimer + .stop((AbstractWebApplicationContext) application + .getContext()); } } } diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index 18cc3f97f4..6ab2748332 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -400,6 +400,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements private void service(WrappedHttpServletRequest request, WrappedHttpServletResponse response) throws ServletException, IOException { + RequestTimer requestTimer = new RequestTimer(); + requestTimer.start(); + AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper( this); @@ -537,8 +540,11 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } finally { Root.setCurrentRoot(null); Application.setCurrentApplication(null); - } + requestTimer + .stop((AbstractWebApplicationContext) application + .getContext()); + } } } diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index bb6e726166..c57e7d8bc1 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -17,6 +17,7 @@ import java.io.Serializable; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.security.GeneralSecurityException; import java.text.CharacterIterator; import java.text.DateFormat; @@ -690,7 +691,7 @@ public abstract class AbstractCommunicationManager implements Serializable { outWriter.print(getSecurityKeyUIDL(request)); } - writeUidlResponse(repaintAll, outWriter, root, analyzeLayouts); + writeUidlResponse(request, repaintAll, outWriter, root, analyzeLayouts); closeJsonMessage(outWriter); @@ -734,7 +735,7 @@ public abstract class AbstractCommunicationManager implements Serializable { } @SuppressWarnings("unchecked") - public void writeUidlResponse(boolean repaintAll, + public void writeUidlResponse(WrappedRequest request, boolean repaintAll, final PrintWriter outWriter, Root root, boolean analyzeLayouts) throws PaintException { ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>(); @@ -807,8 +808,9 @@ public abstract class AbstractCommunicationManager implements Serializable { if (null != state) { // encode and send shared state try { + // FIXME Use declared type JSONArray stateJsonArray = JsonCodec.encode(state, - application); + state.getClass(), application); sharedStates .put(connector.getConnectorId(), stateJsonArray); } catch (JSONException e) { @@ -897,9 +899,10 @@ public abstract class AbstractCommunicationManager implements Serializable { invocationJson.put(invocation.getInterfaceName()); invocationJson.put(invocation.getMethodName()); JSONArray paramJson = new JSONArray(); - for (int i = 0; i < invocation.getParameters().length; ++i) { + for (int i = 0; i < invocation.getParameterTypes().length; ++i) { paramJson.put(JsonCodec.encode( - invocation.getParameters()[i], application)); + invocation.getParameters()[i], + invocation.getParameterTypes()[i], application)); } invocationJson.put(paramJson); rpcCalls.put(invocationJson); @@ -1093,6 +1096,19 @@ public abstract class AbstractCommunicationManager implements Serializable { if (dragAndDropService != null) { dragAndDropService.printJSONResponse(outWriter); } + + writePerformanceData(outWriter); + } + + /** + * Adds the performance timing data (used by TestBench 3) to the UIDL + * response. + */ + private void writePerformanceData(final PrintWriter outWriter) { + AbstractWebApplicationContext ctx = (AbstractWebApplicationContext) application + .getContext(); + outWriter.write(String.format(", \"timings\":[%d, %d]", + ctx.getTotalSessionTime(), ctx.getLastRequestTime())); } private void legacyPaint(PaintTarget paintTarget, @@ -1167,7 +1183,7 @@ public abstract class AbstractCommunicationManager implements Serializable { * The child to check * @return true if the child is visible to the client, false otherwise */ - private boolean isVisible(Component child) { + static boolean isVisible(Component child) { HasComponents parent = child.getParent(); if (parent == null || !child.isVisible()) { return child.isVisible(); @@ -1368,43 +1384,6 @@ public abstract class AbstractCommunicationManager implements Serializable { } /** - * Helper class for parsing variable change RPC calls. - * - * Note that variable changes still only support the old data types and - * partly use Vaadin 6 way of encoding of values. Other RPC method calls - * support more data types. - * - * @since 7.0 - */ - private class VariableChange { - private final String name; - private final Object value; - - public VariableChange(MethodInvocation invocation) throws JSONException { - name = (String) invocation.getParameters()[0]; - value = invocation.getParameters()[1]; - } - - /** - * Returns the variable name for the modification. - * - * @return variable name - */ - public String getName() { - return name; - } - - /** - * Returns the (parsed and converted) value of the updated variable. - * - * @return variable value - */ - public Object getValue() { - return value; - } - } - - /** * Processes a message burst received from the client. * * A burst can contain any number of RPC calls, including legacy variable @@ -1427,108 +1406,93 @@ public abstract class AbstractCommunicationManager implements Serializable { final String burst) { boolean success = true; try { - List<MethodInvocation> invocations = parseInvocations(burst); - - // Perform the method invocations, grouping consecutive variable - // changes for the same Paintable. + Set<Connector> enabledConnectors = new HashSet<Connector>(); - // Combining of variable changes is currently needed to preserve the - // old semantics for any component that relies on them. If the - // support for legacy variable change events is removed, each call - // can be performed separately and thelogic here simplified. + List<MethodInvocation> invocations = parseInvocations(burst); + for (MethodInvocation invocation : invocations) { + final ClientConnector connector = getConnector(app, + invocation.getConnectorId()); + if (connector != null && connector.isConnectorEnabled()) { + enabledConnectors.add(connector); + } + } for (int i = 0; i < invocations.size(); i++) { MethodInvocation invocation = invocations.get(i); - MethodInvocation nextInvocation = null; - if (i + 1 < invocations.size()) { - nextInvocation = invocations.get(i + 1); - } - - final String interfaceName = invocation.getInterfaceName(); + final ClientConnector connector = getConnector(app, + invocation.getConnectorId()); - if (!ApplicationConnection.UPDATE_VARIABLE_INTERFACE - .equals(interfaceName)) { - // handle other RPC calls than variable changes - applyInvocation(app, invocation); + if (connector == null) { + logger.log( + Level.WARNING, + "RPC call to " + invocation.getInterfaceName() + + "." + invocation.getMethodName() + + " received for connector " + + invocation.getConnectorId() + + " but no such connector could be found"); continue; } - final ClientConnector connector = getConnector(app, - invocation.getConnectorId()); - final VariableOwner owner = (VariableOwner) connector; - - boolean connectorEnabled = (connector != null && connector - .isConnectorEnabled()); - - if (owner != null && connectorEnabled) { - VariableChange change = new VariableChange(invocation); - - // TODO could optimize with a single value map if only one - // change for a paintable - - Map<String, Object> m = new HashMap<String, Object>(); - m.put(change.getName(), change.getValue()); - while (nextInvocation != null - && invocation.getConnectorId().equals( - nextInvocation.getConnectorId()) - && ApplicationConnection.UPDATE_VARIABLE_METHOD - .equals(nextInvocation.getMethodName())) { - i++; - invocation = nextInvocation; - change = new VariableChange(invocation); - m.put(change.getName(), change.getValue()); - if (i + 1 < invocations.size()) { - nextInvocation = invocations.get(i + 1); - } else { - nextInvocation = null; - } - } - try { - changeVariables(source, owner, m); - } catch (Exception e) { - Component errorComponent = null; - if (owner instanceof Component) { - errorComponent = (Component) owner; - } else if (owner instanceof DragAndDropService) { - if (m.get("dhowner") instanceof Component) { - errorComponent = (Component) m.get("dhowner"); - } + if (!enabledConnectors.contains(connector)) { + + if (invocation instanceof LegacyChangeVariablesInvocation) { + LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; + // TODO convert window close to a separate RPC call and + // handle above - not a variable change + + // Handle special case where window-close is called + // after the window has been removed from the + // application or the application has closed + Map<String, Object> changes = legacyInvocation + .getVariableChanges(); + if (changes.size() == 1 && changes.containsKey("close") + && Boolean.TRUE.equals(changes.get("close"))) { + // Silently ignore this + continue; } - handleChangeVariablesError(app, errorComponent, e, m); - } - } else { - // TODO convert window close to a separate RPC call and - // handle above - not a variable change - - VariableChange change = new VariableChange(invocation); - - // Handle special case where window-close is called - // after the window has been removed from the - // application or the application has closed - if ("close".equals(change.getName()) - && Boolean.TRUE.equals(change.getValue())) { - // Silently ignore this - continue; } - // Ignore variable change - String msg = "Warning: Ignoring RPC call for "; - if (owner != null) { - msg += "disabled component " + owner.getClass(); - String caption = ((Component) owner).getCaption(); + // Connector is disabled, log a warning and move to the next + String msg = "Ignoring RPC call for disabled connector " + + connector.getClass().getName(); + if (connector instanceof Component) { + String caption = ((Component) connector).getCaption(); if (caption != null) { msg += ", caption=" + caption; } - } else { - msg += "non-existent component, VAR_PID=" - + invocation.getConnectorId(); - // TODO should this cause the message to be ignored? - success = false; } logger.warning(msg); continue; } + + if (invocation instanceof ServerRpcMethodInvocation) { + ServerRpcManager.applyInvocation(connector, + (ServerRpcMethodInvocation) invocation); + } else { + + // All code below is for legacy variable changes + LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; + Map<String, Object> changes = legacyInvocation + .getVariableChanges(); + try { + changeVariables(source, (VariableOwner) connector, + changes); + } catch (Exception e) { + Component errorComponent = null; + if (connector instanceof Component) { + errorComponent = (Component) connector; + } else if (connector instanceof DragAndDropService) { + Object dropHandlerOwner = changes.get("dhowner"); + if (dropHandlerOwner instanceof Component) { + errorComponent = (Component) dropHandlerOwner; + } + } + handleChangeVariablesError(app, errorComponent, e, + changes); + + } + } } } catch (JSONException e) { @@ -1542,34 +1506,6 @@ public abstract class AbstractCommunicationManager implements Serializable { } /** - * Execute an RPC call from the client by finding its target and letting the - * RPC mechanism call the correct method for it. - * - * @param app - * - * @param invocation - */ - protected void applyInvocation(Application app, MethodInvocation invocation) { - Connector c = app.getConnector(invocation.getConnectorId()); - if (c instanceof RpcTarget) { - ServerRpcManager.applyInvocation((RpcTarget) c, invocation); - } else if (c == null) { - logger.log( - Level.WARNING, - "RPC call " + invocation.getInterfaceName() + "." - + invocation.getMethodName() - + " received for connector id " - + invocation.getConnectorId() - + " but no such connector could be found"); - - } else { - logger.log(Level.WARNING, "RPC call received for connector " - + c.getClass().getName() + " (" + c.getConnectorId() - + ") but the connector is not a ServerRpcTarget"); - } - } - - /** * Parse a message burst from the client into a list of MethodInvocation * instances. * @@ -1584,25 +1520,94 @@ public abstract class AbstractCommunicationManager implements Serializable { ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>(); + MethodInvocation previousInvocation = null; // parse JSON to MethodInvocations for (int i = 0; i < invocationsJson.length(); ++i) { + JSONArray invocationJson = invocationsJson.getJSONArray(i); - String connectorId = invocationJson.getString(0); - String interfaceName = invocationJson.getString(1); - String methodName = invocationJson.getString(2); - JSONArray parametersJson = invocationJson.getJSONArray(3); - Object[] parameters = new Object[parametersJson.length()]; - for (int j = 0; j < parametersJson.length(); ++j) { - parameters[j] = JsonCodec.decode( - parametersJson.getJSONArray(j), application); + + MethodInvocation invocation = parseInvocation(invocationJson, + previousInvocation); + if (invocation != null) { + // Can be null iff the invocation was a legacy invocation and it + // was merged with the previous one + invocations.add(invocation); + previousInvocation = invocation; } - MethodInvocation invocation = new MethodInvocation(connectorId, - interfaceName, methodName, parameters); - invocations.add(invocation); } return invocations; } + private MethodInvocation parseInvocation(JSONArray invocationJson, + MethodInvocation previousInvocation) throws JSONException { + String connectorId = invocationJson.getString(0); + String interfaceName = invocationJson.getString(1); + String methodName = invocationJson.getString(2); + + JSONArray parametersJson = invocationJson.getJSONArray(3); + + if (LegacyChangeVariablesInvocation.isLegacyVariableChange( + interfaceName, methodName)) { + if (!(previousInvocation instanceof LegacyChangeVariablesInvocation)) { + previousInvocation = null; + } + + return parseLegacyChangeVariablesInvocation(connectorId, + interfaceName, methodName, + (LegacyChangeVariablesInvocation) previousInvocation, + parametersJson); + } else { + return parseServerRpcInvocation(connectorId, interfaceName, + methodName, parametersJson); + } + + } + + private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation( + String connectorId, String interfaceName, String methodName, + LegacyChangeVariablesInvocation previousInvocation, + JSONArray parametersJson) throws JSONException { + if (parametersJson.length() != 2) { + throw new JSONException( + "Invalid parameters in legacy change variables call. Expected 2, was " + + parametersJson.length()); + } + String variableName = (String) JsonCodec + .decodeInternalType(String.class, true, + parametersJson.getJSONArray(0), application); + Object value = JsonCodec.decodeInternalType( + parametersJson.getJSONArray(1), application); + + if (previousInvocation != null + && previousInvocation.getConnectorId().equals(connectorId)) { + previousInvocation.setVariableChange(variableName, value); + return null; + } else { + return new LegacyChangeVariablesInvocation(connectorId, + variableName, value); + } + } + + private ServerRpcMethodInvocation parseServerRpcInvocation( + String connectorId, String interfaceName, String methodName, + JSONArray parametersJson) throws JSONException { + ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation( + connectorId, interfaceName, methodName, parametersJson.length()); + + Object[] parameters = new Object[parametersJson.length()]; + Type[] declaredRpcMethodParameterTypes = invocation.getMethod() + .getGenericParameterTypes(); + + for (int j = 0; j < parametersJson.length(); ++j) { + JSONArray parameterJson = parametersJson.getJSONArray(j); + Type parameterType = declaredRpcMethodParameterTypes[j]; + parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType, + parameterJson, application); + } + invocation.setParameters(parameters); + return invocation; + } + protected void changeVariables(Object source, final VariableOwner owner, Map<String, Object> m) { owner.changeVariables(source, m); @@ -2126,7 +2131,18 @@ public abstract class AbstractCommunicationManager implements Serializable { String initialUIDL = getInitialUIDL(combinedRequest, root); params.put("uidl", initialUIDL); } - response.getWriter().write(params.toString()); + + // NOTE! GateIn requires, for some weird reason, getOutputStream + // to be used instead of getWriter() (it seems to interpret + // application/json as a binary content type) + final OutputStream out = response.getOutputStream(); + final PrintWriter outWriter = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(out, "UTF-8"))); + + outWriter.write(params.toString()); + // NOTE GateIn requires the buffers to be flushed to work + outWriter.flush(); + out.flush(); } catch (RootRequiresMoreInformationException e) { // Requiring more information at this point is not allowed // TODO handle in a better way @@ -2158,7 +2174,7 @@ public abstract class AbstractCommunicationManager implements Serializable { if (isXSRFEnabled(root.getApplication())) { pWriter.print(getSecurityKeyUIDL(request)); } - writeUidlResponse(true, pWriter, root, false); + writeUidlResponse(request, true, pWriter, root, false); pWriter.print("}"); String initialUIDL = sWriter.toString(); logger.log(Level.FINE, "Initial UIDL:" + initialUIDL); diff --git a/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java b/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java index c8335a8607..c0ae0afc26 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractWebApplicationContext.java @@ -44,6 +44,10 @@ public abstract class AbstractWebApplicationContext implements protected HashMap<Application, AbstractCommunicationManager> applicationToAjaxAppMgrMap = new HashMap<Application, AbstractCommunicationManager>(); + private long totalSessionTime = 0; + + private long lastRequestTime = -1; + public void addTransactionListener(TransactionListener listener) { if (listener != null) { listeners.add(listener); @@ -222,4 +226,30 @@ public abstract class AbstractWebApplicationContext implements return relativeUri.substring(index + 1, next); } + /** + * @return The total time spent servicing requests in this session. + */ + public long getTotalSessionTime() { + return totalSessionTime; + } + + /** + * Sets the time spent servicing the last request in the session and updates + * the total time spent servicing requests in this session. + * + * @param time + * the time spent in the last request. + */ + public void setLastRequestTime(long time) { + lastRequestTime = time; + totalSessionTime += time; + } + + /** + * @return the time spent servicing the last request in this session. + */ + public long getLastRequestTime() { + return lastRequestTime; + } + }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/server/ClientConnector.java b/src/com/vaadin/terminal/gwt/server/ClientConnector.java index cc4c1161a0..7a1f0fad68 100644 --- a/src/com/vaadin/terminal/gwt/server/ClientConnector.java +++ b/src/com/vaadin/terminal/gwt/server/ClientConnector.java @@ -16,7 +16,7 @@ import com.vaadin.terminal.gwt.client.Connector; * @since 7.0.0 * */ -public interface ClientConnector extends Connector { +public interface ClientConnector extends Connector, RpcTarget { /** * Returns the list of pending server to client RPC calls and clears the * list. diff --git a/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java index 2edcb8a9f2..99633a13d6 100644 --- a/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java +++ b/src/com/vaadin/terminal/gwt/server/ClientMethodInvocation.java @@ -5,6 +5,7 @@ package com.vaadin.terminal.gwt.server; import java.io.Serializable; +import java.lang.reflect.Method; /** * Internal class for keeping track of pending server to client method @@ -18,21 +19,27 @@ public class ClientMethodInvocation implements Serializable, private final String interfaceName; private final String methodName; private final Object[] parameters; + private Class<?>[] parameterTypes; - // used for sorting calls between different Paintables in the same Root + // used for sorting calls between different connectors in the same Root private final long sequenceNumber; // TODO may cause problems when clustering etc. private static long counter = 0; public ClientMethodInvocation(ClientConnector connector, - String interfaceName, String methodName, Object[] parameters) { + String interfaceName, Method method, Object[] parameters) { this.connector = connector; this.interfaceName = interfaceName; - this.methodName = methodName; + methodName = method.getName(); + parameterTypes = method.getParameterTypes(); this.parameters = (null != parameters) ? parameters : new Object[0]; sequenceNumber = ++counter; } + public Class<?>[] getParameterTypes() { + return parameterTypes; + } + public ClientConnector getConnector() { return connector; } diff --git a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java index ca499d024d..d3fe5a890b 100644 --- a/src/com/vaadin/terminal/gwt/server/DragAndDropService.java +++ b/src/com/vaadin/terminal/gwt/server/DragAndDropService.java @@ -233,4 +233,9 @@ public class DragAndDropService implements VariableOwner, ClientConnector { public List<ClientMethodInvocation> retrievePendingRpcCalls() { return null; } + + public RpcManager getRpcManager(Class<?> rpcInterface) { + // TODO Use rpc for drag'n'drop + return null; + } } diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java index 1824a16fb2..375cce4161 100644 --- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java +++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java @@ -10,6 +10,8 @@ import java.beans.PropertyDescriptor; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -25,6 +27,7 @@ import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; import com.vaadin.terminal.gwt.client.Connector; import com.vaadin.terminal.gwt.client.communication.JsonEncoder; +import com.vaadin.ui.Component; /** * Decoder for converting RPC parameters and other values from JSON in transfer @@ -36,6 +39,12 @@ public class JsonCodec implements Serializable { private static Map<Class<?>, String> typeToTransportType = new HashMap<Class<?>, String>(); + /** + * Note! This does not contain primitives. + * <p> + */ + private static Map<String, Class<?>> transportTypeToType = new HashMap<String, Class<?>>(); + static { registerType(String.class, JsonEncoder.VTYPE_STRING); registerType(Connector.class, JsonEncoder.VTYPE_CONNECTOR); @@ -49,8 +58,6 @@ public class JsonCodec implements Serializable { registerType(double.class, JsonEncoder.VTYPE_DOUBLE); registerType(Long.class, JsonEncoder.VTYPE_LONG); registerType(long.class, JsonEncoder.VTYPE_LONG); - // transported as string representation - registerType(Enum.class, JsonEncoder.VTYPE_STRING); registerType(String[].class, JsonEncoder.VTYPE_STRINGARRAY); registerType(Object[].class, JsonEncoder.VTYPE_ARRAY); registerType(Map.class, JsonEncoder.VTYPE_MAP); @@ -60,96 +67,260 @@ public class JsonCodec implements Serializable { private static void registerType(Class<?> type, String transportType) { typeToTransportType.put(type, transportType); + if (!type.isPrimitive()) { + transportTypeToType.put(transportType, type); + } + } + + public static boolean isInternalTransportType(String transportType) { + return transportTypeToType.containsKey(transportType); + } + + public static boolean isInternalType(Type type) { + if (type instanceof Class && ((Class<?>) type).isPrimitive()) { + // All primitive types are handled internally + return true; + } + return typeToTransportType.containsKey(getClassForType(type)); + } + + private static Class<?> getClassForType(Type type) { + if (type instanceof ParameterizedType) { + return (Class<?>) (((ParameterizedType) type).getRawType()); + } else { + return (Class<?>) type; + } + } + + public static String getTransportType(JSONArray encodedValue) + throws JSONException { + return encodedValue.getString(0); + } + + private static Class<?> getType(String transportType) { + return transportTypeToType.get(transportType); } /** - * Convert a JSON array with two elements (type and value) into a - * server-side type, recursively if necessary. + * Decodes the given value and type, restricted to using only internal + * types. * - * @param value - * JSON array with two elements + * @param valueAndType * @param application - * mapper between connector ID and {@link Connector} objects - * @return converted value (does not contain JSON types) * @throws JSONException - * if the conversion fails */ - public static Object decode(JSONArray value, Application application) - throws JSONException { - return decodeVariableValue(value.getString(0), value.get(1), + @Deprecated + public static Object decodeInternalType(JSONArray valueAndType, + Application application) throws JSONException { + String transportType = getTransportType(valueAndType); + return decodeInternalType(getType(transportType), true, valueAndType, application); } - private static Object decodeVariableValue(String variableType, - Object value, Application application) throws JSONException { - Object val = null; - // TODO type checks etc. - if (JsonEncoder.VTYPE_ARRAY.equals(variableType)) { - val = decodeArray((JSONArray) value, application); - } else if (JsonEncoder.VTYPE_LIST.equals(variableType)) { - val = decodeList((JSONArray) value, application); - } else if (JsonEncoder.VTYPE_SET.equals(variableType)) { - val = decodeSet((JSONArray) value, application); - } else if (JsonEncoder.VTYPE_MAP_CONNECTOR.equals(variableType)) { - val = decodeConnectorMap((JSONObject) value, application); - } else if (JsonEncoder.VTYPE_MAP.equals(variableType)) { - val = decodeMap((JSONObject) value, application); - } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(variableType)) { - val = decodeStringArray((JSONArray) value); - } else if (JsonEncoder.VTYPE_STRING.equals(variableType)) { - val = value; - } else if (JsonEncoder.VTYPE_INTEGER.equals(variableType)) { - // TODO handle properly - val = Integer.valueOf(String.valueOf(value)); - } else if (JsonEncoder.VTYPE_LONG.equals(variableType)) { - // TODO handle properly - val = Long.valueOf(String.valueOf(value)); - } else if (JsonEncoder.VTYPE_FLOAT.equals(variableType)) { - // TODO handle properly - val = Float.valueOf(String.valueOf(value)); - } else if (JsonEncoder.VTYPE_DOUBLE.equals(variableType)) { - // TODO handle properly - val = Double.valueOf(String.valueOf(value)); - } else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) { - // TODO handle properly - val = Boolean.valueOf(String.valueOf(value)); - } else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) { - val = application.getConnector(String.valueOf(value)); - } else if (JsonEncoder.VTYPE_NULL.equals(variableType)) { - val = null; + public static Object decodeInternalOrCustomType(Type targetType, + JSONArray valueAndType, Application application) + throws JSONException { + if (isInternalType(targetType)) { + return decodeInternalType(targetType, false, valueAndType, + application); } else { - // Try to decode object using fields - return decodeObject(variableType, (JSONObject) value, application); + return decodeCustomType(targetType, valueAndType, application); + } + } + public static Object decodeCustomType(Type targetType, + JSONArray valueAndType, Application application) + throws JSONException { + if (isInternalType(targetType)) { + throw new JSONException("decodeCustomType cannot be used for " + + targetType + ", which is an internal type"); + } + String transportType = getCustomTransportType(getClassForType(targetType)); + String encodedTransportType = valueAndType.getString(0); + if (!transportTypesCompatible(encodedTransportType, transportType)) { + throw new JSONException("Expected a value of type " + transportType + + ", received " + encodedTransportType); } - return val; + // Try to decode object using fields + return decodeObject(targetType, (JSONObject) valueAndType.get(1), + application); } - private static Object decodeMap(JSONObject jsonMap, Application application) - throws JSONException { + /** + * Decodes a value that is of an internal type. + * <p> + * Ensures the encoded value is of the same type as target type. + * </p> + * <p> + * Allows restricting collections so that they must be declared using + * generics. If this is used then all objects in the collection are encoded + * using the declared type. Otherwise only internal types are allowed in + * collections. + * </p> + * + * @param targetType + * The type that should be returned by this method + * @param valueAndType + * The encoded value and type array + * @param application + * A reference to the application + * @param enforceGenericsInCollections + * true if generics should be enforce, false to only allow + * internal types in collections + * @return + * @throws JSONException + */ + public static Object decodeInternalType(Type targetType, + boolean restrictToInternalTypes, JSONArray valueAndType, + Application application) throws JSONException { + String encodedTransportType = valueAndType.getString(0); + if (!isInternalType(targetType)) { + throw new JSONException("Type " + targetType + + " is not a supported internal type."); + } + String transportType = getInternalTransportType(targetType); + if (!transportTypesCompatible(encodedTransportType, transportType)) { + throw new JSONException("Expected a value of type " + targetType + + ", received " + getType(encodedTransportType)); + } + + Object encodedJsonValue = valueAndType.get(1); + + if (JsonEncoder.VTYPE_NULL.equals(encodedTransportType)) { + return null; + } + // Collections + if (JsonEncoder.VTYPE_LIST.equals(transportType)) { + return decodeList(targetType, restrictToInternalTypes, + (JSONArray) encodedJsonValue, application); + } else if (JsonEncoder.VTYPE_SET.equals(transportType)) { + return decodeSet(targetType, restrictToInternalTypes, + (JSONArray) encodedJsonValue, application); + } else if (JsonEncoder.VTYPE_MAP_CONNECTOR.equals(transportType)) { + return decodeConnectorToObjectMap(targetType, + restrictToInternalTypes, (JSONObject) encodedJsonValue, + application); + } else if (JsonEncoder.VTYPE_MAP.equals(transportType)) { + return decodeStringToObjectMap(targetType, restrictToInternalTypes, + (JSONObject) encodedJsonValue, application); + } + + // Arrays + if (JsonEncoder.VTYPE_ARRAY.equals(transportType)) { + + return decodeObjectArray(targetType, (JSONArray) encodedJsonValue, + application); + + } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(transportType)) { + return decodeStringArray((JSONArray) encodedJsonValue); + } + + // Special Vaadin types + + String stringValue = String.valueOf(encodedJsonValue); + + if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) { + return application.getConnector(stringValue); + } + + // Standard Java types + + if (JsonEncoder.VTYPE_STRING.equals(transportType)) { + return stringValue; + } else if (JsonEncoder.VTYPE_INTEGER.equals(transportType)) { + return Integer.valueOf(stringValue); + } else if (JsonEncoder.VTYPE_LONG.equals(transportType)) { + return Long.valueOf(stringValue); + } else if (JsonEncoder.VTYPE_FLOAT.equals(transportType)) { + return Float.valueOf(stringValue); + } else if (JsonEncoder.VTYPE_DOUBLE.equals(transportType)) { + return Double.valueOf(stringValue); + } else if (JsonEncoder.VTYPE_BOOLEAN.equals(transportType)) { + return Boolean.valueOf(stringValue); + } + + throw new JSONException("Unknown type " + transportType); + } + + private static boolean transportTypesCompatible( + String encodedTransportType, String transportType) { + if (encodedTransportType == null) { + return false; + } + if (encodedTransportType.equals(transportType)) { + return true; + } + if (encodedTransportType.equals(JsonEncoder.VTYPE_NULL)) { + return true; + } + + return false; + } + + @Deprecated + private static Map<String, Object> decodeStringToObjectMap(Type targetType, + boolean restrictToInternalTypes, JSONObject jsonMap, + Application application) throws JSONException { HashMap<String, Object> map = new HashMap<String, Object>(); Iterator<String> it = jsonMap.keys(); while (it.hasNext()) { String key = it.next(); - map.put(key, decode(jsonMap.getJSONArray(key), application)); + JSONArray encodedValueAndType = jsonMap.getJSONArray(key); + Object decodedChild = decodeChild(targetType, + restrictToInternalTypes, 1, encodedValueAndType, + application); + map.put(key, decodedChild); } return map; } - private static Object decodeConnectorMap(JSONObject jsonMap, - Application application) throws JSONException { + @Deprecated + private static Map<Connector, Object> decodeConnectorToObjectMap( + Type targetType, boolean restrictToInternalTypes, + JSONObject jsonMap, Application application) throws JSONException { HashMap<Connector, Object> map = new HashMap<Connector, Object>(); Iterator<String> it = jsonMap.keys(); while (it.hasNext()) { String connectorId = it.next(); Connector connector = application.getConnector(connectorId); - map.put(connector, - decode(jsonMap.getJSONArray(connectorId), application)); + JSONArray encodedValueAndType = jsonMap.getJSONArray(connectorId); + Object decodedChild = decodeChild(targetType, + restrictToInternalTypes, 1, encodedValueAndType, + application); + map.put(connector, decodedChild); } return map; } + /** + * @param targetType + * @param restrictToInternalTypes + * @param typeIndex + * The index of a generic type to use to define the child type + * that should be decoded + * @param encodedValueAndType + * @param application + * @return + * @throws JSONException + */ + private static Object decodeChild(Type targetType, + boolean restrictToInternalTypes, int typeIndex, + JSONArray encodedValueAndType, Application application) + throws JSONException { + if (!restrictToInternalTypes && targetType instanceof ParameterizedType) { + Type childType = ((ParameterizedType) targetType) + .getActualTypeArguments()[typeIndex]; + // Only decode the given type + return decodeInternalOrCustomType(childType, encodedValueAndType, + application); + } else { + // Only internal types when not enforcing a given type to avoid + // security issues + return decodeInternalType(encodedValueAndType, application); + } + } + private static String[] decodeStringArray(JSONArray jsonArray) throws JSONException { int length = jsonArray.length(); @@ -160,43 +331,93 @@ public class JsonCodec implements Serializable { return tokens.toArray(new String[tokens.size()]); } - private static Object decodeArray(JSONArray jsonArray, - Application application) throws JSONException { - List list = decodeList(jsonArray, application); + private static Object[] decodeObjectArray(Type targetType, + JSONArray jsonArray, Application application) throws JSONException { + List list = decodeList(List.class, true, jsonArray, application); return list.toArray(new Object[list.size()]); } - private static List<Object> decodeList(JSONArray jsonArray, + private static List<Object> decodeList(Type targetType, + boolean restrictToInternalTypes, JSONArray jsonArray, Application application) throws JSONException { List<Object> list = new ArrayList<Object>(); for (int i = 0; i < jsonArray.length(); ++i) { // each entry always has two elements: type and value - JSONArray entryArray = jsonArray.getJSONArray(i); - list.add(decode(entryArray, application)); + JSONArray encodedValueAndType = jsonArray.getJSONArray(i); + Object decodedChild = decodeChild(targetType, + restrictToInternalTypes, 0, encodedValueAndType, + application); + list.add(decodedChild); } return list; } - private static Set<Object> decodeSet(JSONArray jsonArray, + private static Set<Object> decodeSet(Type targetType, + boolean restrictToInternalTypes, JSONArray jsonArray, Application application) throws JSONException { HashSet<Object> set = new HashSet<Object>(); - set.addAll(decodeList(jsonArray, application)); + set.addAll(decodeList(List.class, restrictToInternalTypes, jsonArray, + application)); return set; } /** - * Encode a value to a JSON representation for transport from the server to - * the client. + * Returns the name that should be used as field name in the JSON. We strip + * "set" from the setter, keeping the result - this is easy to do on both + * server and client, avoiding some issues with cASE. E.g setZIndex() + * becomes "ZIndex". Also ensures that both getter and setter are present, + * returning null otherwise. * - * @param value - * value to convert - * @param application - * mapper between connector ID and {@link Connector} objects - * @return JSON representation of the value - * @throws JSONException - * if encoding a value fails (e.g. NaN or infinite number) + * @param pd + * @return the name to be used or null if both getter and setter are not + * found. */ - public static JSONArray encode(Object value, Application application) + private static String getTransportFieldName(PropertyDescriptor pd) { + if (pd.getReadMethod() == null || pd.getWriteMethod() == null) { + return null; + } + return pd.getWriteMethod().getName().substring(3); + } + + private static Object decodeObject(Type targetType, + JSONObject serializedObject, Application application) + throws JSONException { + + Class<?> targetClass = getClassForType(targetType); + try { + Object decodedObject = targetClass.newInstance(); + for (PropertyDescriptor pd : Introspector.getBeanInfo(targetClass) + .getPropertyDescriptors()) { + + String fieldName = getTransportFieldName(pd); + if (fieldName == null) { + continue; + } + JSONArray encodedFieldValue = serializedObject + .getJSONArray(fieldName); + Type fieldType = pd.getReadMethod().getGenericReturnType(); + Object decodedFieldValue = decodeInternalOrCustomType( + fieldType, encodedFieldValue, application); + + pd.getWriteMethod().invoke(decodedObject, decodedFieldValue); + } + + return decodedObject; + } catch (IllegalArgumentException e) { + throw new JSONException(e); + } catch (IllegalAccessException e) { + throw new JSONException(e); + } catch (InvocationTargetException e) { + throw new JSONException(e); + } catch (InstantiationException e) { + throw new JSONException(e); + } catch (IntrospectionException e) { + throw new JSONException(e); + } + } + + @Deprecated + private static JSONArray encode(Object value, Application application) throws JSONException { return encode(value, null, application); } @@ -205,14 +426,14 @@ public class JsonCodec implements Serializable { Application application) throws JSONException { if (null == value) { - return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL); + return encodeNull(); } if (valueType == null) { valueType = value.getClass(); } - String transportType = getTransportType(valueType); + String internalTransportType = getInternalTransportType(valueType); if (value instanceof String[]) { String[] array = (String[]) value; JSONArray jsonArray = new JSONArray(); @@ -225,16 +446,16 @@ public class JsonCodec implements Serializable { } else if (value instanceof Boolean) { return combineTypeAndValue(JsonEncoder.VTYPE_BOOLEAN, value); } else if (value instanceof Number) { - return combineTypeAndValue(transportType, value); + return combineTypeAndValue(internalTransportType, value); } else if (value instanceof Collection) { - if (transportType == null) { + if (internalTransportType == null) { throw new RuntimeException( "Unable to serialize unsupported type: " + valueType); } Collection<?> collection = (Collection<?>) value; JSONArray jsonArray = encodeCollection(collection, application); - return combineTypeAndValue(transportType, jsonArray); + return combineTypeAndValue(internalTransportType, jsonArray); } else if (value instanceof Object[]) { Object[] array = (Object[]) value; JSONArray jsonArray = encodeArrayContents(array, application); @@ -252,18 +473,28 @@ public class JsonCodec implements Serializable { } } else if (value instanceof Connector) { Connector connector = (Connector) value; + if (value instanceof Component + && !(AbstractCommunicationManager + .isVisible((Component) value))) { + return encodeNull(); + } return combineTypeAndValue(JsonEncoder.VTYPE_CONNECTOR, connector.getConnectorId()); - } else if (transportType != null) { - return combineTypeAndValue(transportType, String.valueOf(value)); + } else if (internalTransportType != null) { + return combineTypeAndValue(internalTransportType, + String.valueOf(value)); } else { // Any object that we do not know how to encode we encode by looping // through fields - return combineTypeAndValue(valueType.getName(), + return combineTypeAndValue(getCustomTransportType(valueType), encodeObject(value, application)); } } + private static JSONArray encodeNull() { + return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL); + } + private static Object encodeObject(Object value, Application application) throws JSONException { JSONObject jsonMap = new JSONObject(); @@ -288,68 +519,11 @@ public class JsonCodec implements Serializable { return jsonMap; } - /** - * Returns the name that should be used as field name in the JSON. We strip - * "set" from the setter, keeping the result - this is easy to do on both - * server and client, avoiding some issues with cASE. E.g setZIndex() - * becomes "ZIndex". Also ensures that both getter and setter are present, - * returning null otherwise. - * - * @param pd - * @return the name to be used or null if both getter and setter are not - * found. - */ - private static String getTransportFieldName(PropertyDescriptor pd) { - if (pd.getReadMethod() == null || pd.getWriteMethod() == null) { - return null; - } - return pd.getWriteMethod().getName().substring(3); - } - - private static Object decodeObject(String type, - JSONObject serializedObject, Application application) - throws JSONException { - - Class<?> cls; - try { - cls = Class.forName(type); - - Object decodedObject = cls.newInstance(); - for (PropertyDescriptor pd : Introspector.getBeanInfo(cls) - .getPropertyDescriptors()) { - - String fieldName = getTransportFieldName(pd); - if (fieldName == null) { - continue; - } - JSONArray encodedObject = serializedObject - .getJSONArray(fieldName); - pd.getWriteMethod().invoke(decodedObject, - decode(encodedObject, application)); - } - - return decodedObject; - } catch (ClassNotFoundException e) { - throw new JSONException(e); - } catch (IllegalArgumentException e) { - throw new JSONException(e); - } catch (IllegalAccessException e) { - throw new JSONException(e); - } catch (InvocationTargetException e) { - throw new JSONException(e); - } catch (InstantiationException e) { - throw new JSONException(e); - } catch (IntrospectionException e) { - throw new JSONException(e); - } - } - private static JSONArray encodeArrayContents(Object[] array, Application application) throws JSONException { JSONArray jsonArray = new JSONArray(); for (Object o : array) { - // TODO handle object graph loops? - jsonArray.put(encode(o, application)); + jsonArray.put(encode(o, null, application)); } return jsonArray; } @@ -358,7 +532,6 @@ public class JsonCodec implements Serializable { Application application) throws JSONException { JSONArray jsonArray = new JSONArray(); for (Object o : collection) { - // TODO handle object graph loops? jsonArray.put(encode(o, application)); } return jsonArray; @@ -378,7 +551,7 @@ public class JsonCodec implements Serializable { "Only maps with String/Connector keys are currently supported (#8602)"); } - jsonMap.put((String) mapKey, encode(mapValue, application)); + jsonMap.put((String) mapKey, encode(mapValue, null, application)); } return jsonMap; } @@ -395,21 +568,6 @@ public class JsonCodec implements Serializable { } /** - * Gets the transport type for the value. Returns null if no transport type - * can be found. - * - * @param value - * @return - * @throws JSONException - */ - private static String getTransportType(Object value) { - if (null == value) { - return JsonEncoder.VTYPE_NULL; - } - return getTransportType(value.getClass()); - } - - /** * Gets the transport type for the given class. Returns null if no transport * type can be found. * @@ -418,9 +576,12 @@ public class JsonCodec implements Serializable { * @return * @throws JSONException */ - private static String getTransportType(Class<?> valueType) { - return typeToTransportType.get(valueType); + private static String getInternalTransportType(Type valueType) { + return typeToTransportType.get(getClassForType(valueType)); + } + private static String getCustomTransportType(Class<?> targetType) { + return targetType.getName(); } } diff --git a/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java b/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java new file mode 100644 index 0000000000..42fa3ab5a5 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java @@ -0,0 +1,38 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.server; + +import java.util.HashMap; +import java.util.Map; + +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.communication.MethodInvocation; + +public class LegacyChangeVariablesInvocation extends MethodInvocation { + private Map<String, Object> variableChanges = new HashMap<String, Object>(); + + public LegacyChangeVariablesInvocation(String connectorId, + String variableName, Object value) { + super(connectorId, ApplicationConnection.UPDATE_VARIABLE_INTERFACE, + ApplicationConnection.UPDATE_VARIABLE_METHOD); + setVariableChange(variableName, value); + } + + public static boolean isLegacyVariableChange(String interfaceName, + String methodName) { + return ApplicationConnection.UPDATE_VARIABLE_METHOD + .equals(interfaceName) + && ApplicationConnection.UPDATE_VARIABLE_METHOD + .equals(methodName); + } + + public void setVariableChange(String name, Object value) { + variableChanges.put(name, value); + } + + public Map<String, Object> getVariableChanges() { + return variableChanges; + } + +} diff --git a/src/com/vaadin/terminal/gwt/server/RequestTimer.java b/src/com/vaadin/terminal/gwt/server/RequestTimer.java new file mode 100644 index 0000000000..6c0edec466 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/server/RequestTimer.java @@ -0,0 +1,43 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.server; + +import java.io.Serializable; + +/** + * Times the handling of requests and stores the information as an attribute in + * the request. The timing info is later passed on to the client in the UIDL and + * the client provides JavaScript API for accessing this data from e.g. + * TestBench. + * + * @author Jonatan Kronqvist / Vaadin Ltd + */ +public class RequestTimer implements Serializable { + private long requestStartTime = 0; + + /** + * Starts the timing of a request. This should be called before any + * processing of the request. + */ + public void start() { + requestStartTime = System.nanoTime(); + } + + /** + * Stops the timing of a request. This should be called when all processing + * of a request has finished. + * + * @param context + */ + public void stop(AbstractWebApplicationContext context) { + // Measure and store the total handling time. This data can be + // used in TestBench 3 tests. + long time = (System.nanoTime() - requestStartTime) / 1000000; + + // The timings must be stored in the context, since a new + // RequestTimer is created for every request. + context.setLastRequestTime(time); + } +} diff --git a/src/com/vaadin/terminal/gwt/server/RpcManager.java b/src/com/vaadin/terminal/gwt/server/RpcManager.java index 5fcfda50a5..d240ab8467 100644 --- a/src/com/vaadin/terminal/gwt/server/RpcManager.java +++ b/src/com/vaadin/terminal/gwt/server/RpcManager.java @@ -6,8 +6,6 @@ package com.vaadin.terminal.gwt.server; import java.io.Serializable; -import com.vaadin.terminal.gwt.client.communication.MethodInvocation; - /** * Server side RPC manager that can invoke methods based on RPC calls received * from the client. @@ -15,5 +13,5 @@ import com.vaadin.terminal.gwt.client.communication.MethodInvocation; * @since 7.0 */ public interface RpcManager extends Serializable { - public void applyInvocation(MethodInvocation invocation); + public void applyInvocation(ServerRpcMethodInvocation invocation); } diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java index cdab4b327f..07f83864c2 100644 --- a/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java +++ b/src/com/vaadin/terminal/gwt/server/ServerRpcManager.java @@ -8,12 +8,10 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.terminal.gwt.client.Connector; -import com.vaadin.terminal.gwt.client.communication.MethodInvocation; /** * Server side RPC manager that handles RPC calls coming from the client. @@ -26,13 +24,9 @@ import com.vaadin.terminal.gwt.client.communication.MethodInvocation; */ public class ServerRpcManager<T> implements RpcManager { - private final RpcTarget target; private final T implementation; private final Class<T> rpcInterface; - private static final Map<String, Method> invocationMethodCache = new ConcurrentHashMap<String, Method>( - 128, 0.75f, 1); - private static final Map<Class<?>, Class<?>> boxedTypes = new HashMap<Class<?>, Class<?>>(); static { try { @@ -59,9 +53,7 @@ public class ServerRpcManager<T> implements RpcManager { * @param rpcInterface * RPC interface type */ - public ServerRpcManager(RpcTarget target, T implementation, - Class<T> rpcInterface) { - this.target = target; + public ServerRpcManager(T implementation, Class<T> rpcInterface) { this.implementation = implementation; this.rpcInterface = rpcInterface; } @@ -76,39 +68,23 @@ public class ServerRpcManager<T> implements RpcManager { * method invocation to perform */ public static void applyInvocation(RpcTarget target, - MethodInvocation invocation) { - try { - Class<?> rpcInterfaceClass = Class.forName(invocation - .getInterfaceName()); - RpcManager manager = target.getRpcManager(rpcInterfaceClass); - if (manager != null) { - manager.applyInvocation(invocation); - } else { - getLogger() - .log(Level.WARNING, - "RPC call received for RpcTarget " - + target.getClass().getName() - + " (" - + invocation.getConnectorId() - + ") but the target has not registered any RPC interfaces"); - } - } catch (ClassNotFoundException e) { - throw new RuntimeException("Class for RPC interface " - + invocation.getInterfaceName() + " of the target " - + target + " could not be found."); + ServerRpcMethodInvocation invocation) { + RpcManager manager = target.getRpcManager(invocation + .getInterfaceClass()); + if (manager != null) { + manager.applyInvocation(invocation); + } else { + getLogger() + .log(Level.WARNING, + "RPC call received for RpcTarget " + + target.getClass().getName() + + " (" + + invocation.getConnectorId() + + ") but the target has not registered any RPC interfaces"); } } /** - * Returns the RPC target of this RPC manager instance. - * - * @return RpcTarget, typically a {@link Connector} - */ - public RpcTarget getTarget() { - return target; - } - - /** * Returns the RPC interface implementation for the RPC target. * * @return RPC interface implementation @@ -133,21 +109,11 @@ public class ServerRpcManager<T> implements RpcManager { * @param invocation * method invocation to perform */ - public void applyInvocation(MethodInvocation invocation) { - String methodName = invocation.getMethodName(); - // here, we already know that the interface is an rpcInterface - Object[] arguments = invocation.getParameters(); - - Method method = findInvocationMethod(rpcInterface, methodName, - arguments.length); - if (method == null) { - throw new RuntimeException(implementation + " does not contain " - + rpcInterface.getName() + "." + methodName + " with " - + arguments.length + " parameters"); - } - + public void applyInvocation(ServerRpcMethodInvocation invocation) { + Method method = invocation.getMethod(); Class<?>[] parameterTypes = method.getParameterTypes(); Object[] args = new Object[parameterTypes.length]; + Object[] arguments = invocation.getParameters(); for (int i = 0; i < args.length; i++) { // no conversion needed for basic cases // Class<?> type = parameterTypes[i]; @@ -159,41 +125,10 @@ public class ServerRpcManager<T> implements RpcManager { try { method.invoke(implementation, args); } catch (Exception e) { - throw new RuntimeException(methodName, e); - } - } - - private Method findInvocationMethod(Class<?> targetType, String methodName, - int parameterCount) { - // TODO currently only using method name and number of parameters as the - // signature - String signature = targetType.getName() + "." + methodName + "(" - + parameterCount; - Method invocationMethod = invocationMethodCache.get(signature); - - if (invocationMethod == null) { - invocationMethod = doFindInvocationMethod(targetType, methodName, - parameterCount); - - if (invocationMethod != null) { - invocationMethodCache.put(signature, invocationMethod); - } - } - - return invocationMethod; - } - - private Method doFindInvocationMethod(Class<?> targetType, - String methodName, int parameterCount) { - Method[] methods = targetType.getMethods(); - for (Method method : methods) { - Class<?>[] parameterTypes = method.getParameterTypes(); - if (method.getName().equals(methodName) - && parameterTypes.length == parameterCount) { - return method; - } + throw new RuntimeException("Unable to invoke method " + + invocation.getMethodName() + " in " + + invocation.getInterfaceName(), e); } - return null; } private static Logger getLogger() { diff --git a/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java new file mode 100644 index 0000000000..6f278f7797 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/server/ServerRpcMethodInvocation.java @@ -0,0 +1,107 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.server; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.vaadin.terminal.gwt.client.communication.MethodInvocation; +import com.vaadin.terminal.gwt.client.communication.ServerRpc; + +public class ServerRpcMethodInvocation extends MethodInvocation { + + private static final Map<String, Method> invocationMethodCache = new ConcurrentHashMap<String, Method>( + 128, 0.75f, 1); + + private final Method method; + + private Class<? extends ServerRpc> interfaceClass; + + public ServerRpcMethodInvocation(String connectorId, String interfaceName, + String methodName, int parameterCount) { + super(connectorId, interfaceName, methodName); + + interfaceClass = findClass(); + method = findInvocationMethod(interfaceClass, methodName, + parameterCount); + } + + private Class<? extends ServerRpc> findClass() { + try { + Class<?> rpcInterface = Class.forName(getInterfaceName()); + if (!ServerRpc.class.isAssignableFrom(rpcInterface)) { + throw new IllegalArgumentException("The interface " + + getInterfaceName() + "is not a server RPC interface."); + } + return (Class<? extends ServerRpc>) rpcInterface; + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("The server RPC interface " + + getInterfaceName() + " could not be found", e); + } finally { + + } + } + + public Class<? extends ServerRpc> getInterfaceClass() { + return interfaceClass; + } + + public Method getMethod() { + return method; + } + + /** + * Tries to find the method from the cache or alternatively by invoking + * {@link #doFindInvocationMethod(Class, String, int)} and updating the + * cache. + * + * @param targetType + * @param methodName + * @param parameterCount + * @return + */ + private Method findInvocationMethod(Class<?> targetType, String methodName, + int parameterCount) { + // TODO currently only using method name and number of parameters as the + // signature + String signature = targetType.getName() + "." + methodName + "(" + + parameterCount; + Method invocationMethod = invocationMethodCache.get(signature); + + if (invocationMethod == null) { + invocationMethod = doFindInvocationMethod(targetType, methodName, + parameterCount); + + if (invocationMethod != null) { + invocationMethodCache.put(signature, invocationMethod); + } + } + + return invocationMethod; + } + + /** + * Tries to find the method from the class by looping through available + * methods. + * + * @param targetType + * @param methodName + * @param parameterCount + * @return + */ + private Method doFindInvocationMethod(Class<?> targetType, + String methodName, int parameterCount) { + Method[] methods = targetType.getMethods(); + for (Method method : methods) { + Class<?>[] parameterTypes = method.getParameterTypes(); + if (method.getName().equals(methodName) + && parameterTypes.length == parameterCount) { + return method; + } + } + return null; + } + +} diff --git a/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java index 2c9828b66b..3838695aa3 100644 --- a/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java +++ b/src/com/vaadin/terminal/gwt/server/WrappedPortletRequest.java @@ -13,6 +13,7 @@ import javax.portlet.ClientDataRequest; import javax.portlet.PortletRequest; import javax.portlet.ResourceRequest; +import com.vaadin.Application; import com.vaadin.terminal.CombinedRequest; import com.vaadin.terminal.DeploymentConfiguration; import com.vaadin.terminal.WrappedRequest; @@ -118,8 +119,21 @@ public class WrappedPortletRequest implements WrappedRequest { } public BrowserDetails getBrowserDetails() { - // No browserDetails available for normal requests - return null; + return new BrowserDetails() { + public String getUriFragment() { + return null; + } + + public String getWindowName() { + return null; + } + + public WebBrowser getWebBrowser() { + PortletApplicationContext2 context = (PortletApplicationContext2) Application + .getCurrentApplication().getContext(); + return context.getBrowser(); + } + }; } public Locale getLocale() { diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java index e09545962b..013df4710c 100644 --- a/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java +++ b/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java @@ -5,6 +5,7 @@ package com.vaadin.terminal.gwt.widgetsetutils; import java.io.PrintWriter; +import java.io.Serializable; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -52,6 +53,8 @@ public class SerializerMapGenerator extends Generator { TypeOracle typeOracle = context.getTypeOracle(); Set<JClassType> typesNeedingSerializers = findTypesNeedingSerializers( typeOracle, logger); + warnIfNotJavaSerializable(typesNeedingSerializers, typeOracle, + logger); Set<JClassType> typesWithExistingSerializers = findTypesWithExistingSerializers( typeOracle, logger); Set<JClassType> serializerMappings = new HashSet<JClassType>(); @@ -77,6 +80,34 @@ public class SerializerMapGenerator extends Generator { return packageName + "." + className; } + /** + * Emits a warning for all classes that are used in communication but do not + * implement java.io.Serializable. Implementing java.io.Serializable is not + * needed for communication but for the server side Application to be + * serializable i.e. work in GAE for instance. + * + * @param typesNeedingSerializers + * @param typeOracle + * @param logger + */ + private void warnIfNotJavaSerializable( + Set<JClassType> typesNeedingSerializers, TypeOracle typeOracle, + TreeLogger logger) { + JClassType javaSerializable = typeOracle.findType(Serializable.class + .getName()); + for (JClassType type : typesNeedingSerializers) { + boolean serializable = type.isAssignableTo(javaSerializable); + if (!serializable) { + logger.log( + Type.ERROR, + type + + " is used in RPC or shared state but does not implement " + + Serializable.class.getName() + + ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type."); + } + } + } + private Set<JClassType> findTypesWithExistingSerializers( TypeOracle typeOracle, TreeLogger logger) { JClassType serializerInterface = typeOracle @@ -233,8 +264,9 @@ public class SerializerMapGenerator extends Generator { return; } - if (serializableTypes.contains(type)) + if (serializableTypes.contains(type)) { return; + } JClassType typeClass = type.isClass(); if (typeClass != null) { diff --git a/src/com/vaadin/ui/AbsoluteLayout.java b/src/com/vaadin/ui/AbsoluteLayout.java index e548798abe..9ba005f75a 100644 --- a/src/com/vaadin/ui/AbsoluteLayout.java +++ b/src/com/vaadin/ui/AbsoluteLayout.java @@ -16,7 +16,7 @@ import com.vaadin.terminal.Sizeable; import com.vaadin.terminal.gwt.client.Connector; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.absolutelayout.AbsoluteLayoutServerRPC; +import com.vaadin.terminal.gwt.client.ui.absolutelayout.AbsoluteLayoutServerRpc; import com.vaadin.terminal.gwt.client.ui.absolutelayout.AbsoluteLayoutState; /** @@ -28,7 +28,7 @@ import com.vaadin.terminal.gwt.client.ui.absolutelayout.AbsoluteLayoutState; public class AbsoluteLayout extends AbstractLayout implements LayoutClickNotifier { - private AbsoluteLayoutServerRPC rpc = new AbsoluteLayoutServerRPC() { + private AbsoluteLayoutServerRpc rpc = new AbsoluteLayoutServerRpc() { public void layoutClick(MouseEventDetails mouseDetails, Connector clickedConnector) { diff --git a/src/com/vaadin/ui/AbstractComponent.java b/src/com/vaadin/ui/AbstractComponent.java index 83e6f54ad3..79a07ae00e 100644 --- a/src/com/vaadin/ui/AbstractComponent.java +++ b/src/com/vaadin/ui/AbstractComponent.java @@ -534,6 +534,31 @@ public abstract class AbstractComponent implements Component, MethodEventSource return parent; } + /** + * Returns the closest ancestor with the given type. + * <p> + * To find the Window that contains the component, use {@code Window w = + * getParent(Window.class);} + * </p> + * + * @param <T> + * The type of the ancestor + * @param parentType + * The ancestor class we are looking for + * @return The first ancestor that can be assigned to the given class. Null + * if no ancestor with the correct type could be found. + */ + public <T extends HasComponents> T findAncestor(Class<T> parentType) { + HasComponents p = getParent(); + while (p != null) { + if (parentType.isAssignableFrom(p.getClass())) { + return parentType.cast(p); + } + p = p.getParent(); + } + return null; + } + /* * Sets the parent component. Don't add a JavaDoc comment here, we use the * default documentation from implemented interface. @@ -1461,7 +1486,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource private void setActionManagerViewer() { if (actionManager != null && getRoot() != null) { // Attached and has action manager - Window w = findParentOfType(Window.class, this); + Window w = findAncestor(Window.class); if (w != null) { actionManager.setViewer(w); } else { @@ -1471,32 +1496,6 @@ public abstract class AbstractComponent implements Component, MethodEventSource } - /** - * Helper method for finding the first parent component of a given type. - * Useful e.g. for finding the Window the component is inside. - * - * @param <T> - * @param parentType - * The type to look for - * @param c - * The target component - * @return A parent component of type {@literal parentType} or null if no - * parent component in the hierarchy can be assigned to the given - * type. - */ - private static <T extends Component> T findParentOfType( - Class<T> parentType, Component c) { - Component p = c.getParent(); - if (p == null) { - return null; - } - - if (parentType.isAssignableFrom(p.getClass())) { - return (T) p; - } - return findParentOfType(parentType, p); - } - public void addShortcutListener(ShortcutListener shortcut) { getActionManager().addAction(shortcut); } @@ -1522,7 +1521,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource * registered */ protected <T> void registerRpc(T implementation, Class<T> rpcInterfaceType) { - rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>(this, + rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>( implementation, rpcInterfaceType)); } @@ -1597,7 +1596,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - addMethodInvocationToQueue(rpcInterfaceName, method.getName(), args); + addMethodInvocationToQueue(rpcInterfaceName, method, args); // TODO no need to do full repaint if only RPC calls requestRepaint(); return null; @@ -1618,10 +1617,10 @@ public abstract class AbstractComponent implements Component, MethodEventSource * @since 7.0 */ protected void addMethodInvocationToQueue(String interfaceName, - String methodName, Object[] parameters) { + Method method, Object[] parameters) { // add to queue pendingInvocations.add(new ClientMethodInvocation(this, interfaceName, - methodName, parameters)); + method, parameters)); } /** diff --git a/src/com/vaadin/ui/AbstractComponentContainer.java b/src/com/vaadin/ui/AbstractComponentContainer.java index b597451a57..1c857a03cd 100644 --- a/src/com/vaadin/ui/AbstractComponentContainer.java +++ b/src/com/vaadin/ui/AbstractComponentContainer.java @@ -215,17 +215,6 @@ public abstract class AbstractComponentContainer extends AbstractComponent } @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - if (getParent() != null && !getParent().isEnabled()) { - // some ancestor still disabled, don't update children - return; - } else { - requestRepaintAll(); - } - } - - @Override public void setVisible(boolean visible) { if (getState().isVisible() == visible) { return; @@ -379,6 +368,15 @@ public abstract class AbstractComponentContainer extends AbstractComponent */ public static void requestRepaintAll(HasComponents container) { container.requestRepaint(); + if (container instanceof Panel) { + Panel p = (Panel) container; + // #2924 Panel is invalid, really invalid. + // Panel.getComponentIterator returns the children of content, not + // of Panel... + if (p.getContent() != null) { + p.getContent().requestRepaint(); + } + } for (Iterator<Component> childIterator = container .getComponentIterator(); childIterator.hasNext();) { Component c = childIterator.next(); diff --git a/src/com/vaadin/ui/AbstractOrderedLayout.java b/src/com/vaadin/ui/AbstractOrderedLayout.java index 0f2f670331..3606fa6572 100644 --- a/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -19,7 +19,7 @@ import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.gwt.client.Connector; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutServerRPC; +import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutServerRpc; import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutState; @SuppressWarnings("serial") @@ -27,7 +27,7 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier, Vaadin6Component { - private AbstractOrderedLayoutServerRPC rpc = new AbstractOrderedLayoutServerRPC() { + private AbstractOrderedLayoutServerRpc rpc = new AbstractOrderedLayoutServerRpc() { public void layoutClick(MouseEventDetails mouseDetails, Connector clickedConnector) { diff --git a/src/com/vaadin/ui/AbstractSplitPanel.java b/src/com/vaadin/ui/AbstractSplitPanel.java index 5eb46b85a9..5205952621 100644 --- a/src/com/vaadin/ui/AbstractSplitPanel.java +++ b/src/com/vaadin/ui/AbstractSplitPanel.java @@ -13,7 +13,7 @@ import com.vaadin.event.MouseEvents.ClickEvent; import com.vaadin.terminal.Sizeable; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.ClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelRPC; +import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelRpc; import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelState; import com.vaadin.terminal.gwt.client.ui.splitpanel.AbstractSplitPanelState.SplitterState; import com.vaadin.tools.ReflectTools; @@ -33,7 +33,7 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { private Unit posUnit; - private AbstractSplitPanelRPC rpc = new AbstractSplitPanelRPC() { + private AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() { public void splitterClick(MouseEventDetails mouseDetails) { fireEvent(new SplitterClickEvent(AbstractSplitPanel.this, @@ -71,7 +71,6 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { return null; } i++; - AbstractSplitPanelState state = getState(); if (i == 1) { return (getFirstComponent() == null ? getSecondComponent() : getFirstComponent()); diff --git a/src/com/vaadin/ui/Button.java b/src/com/vaadin/ui/Button.java index f5e45ef3ef..876fe593e2 100644 --- a/src/com/vaadin/ui/Button.java +++ b/src/com/vaadin/ui/Button.java @@ -489,4 +489,33 @@ public class Button extends AbstractComponent implements return (ButtonState) super.getState(); } + /** + * Set whether the caption text is rendered as HTML or not. You might need + * to retheme button to allow higher content than the original text style. + * + * If set to true, the captions are passed to the browser as html and the + * developer is responsible for ensuring no harmful html is used. If set to + * false, the content is passed to the browser as plain text. + * + * @param htmlContentAllowed + * <code>true</code> if caption is rendered as HTML, + * <code>false</code> otherwise + */ + public void setHtmlContentAllowed(boolean htmlContentAllowed) { + if (getState().isHtmlContentAllowed() != htmlContentAllowed) { + getState().setHtmlContentAllowed(htmlContentAllowed); + requestRepaint(); + } + } + + /** + * Return HTML rendering setting + * + * @return <code>true</code> if the caption text is to be rendered as HTML, + * <code>false</code> otherwise + */ + public boolean isHtmlContentAllowed() { + return getState().isHtmlContentAllowed(); + } + } diff --git a/src/com/vaadin/ui/Component.java b/src/com/vaadin/ui/Component.java index eacf17b6a7..3632c4ca5e 100644 --- a/src/com/vaadin/ui/Component.java +++ b/src/com/vaadin/ui/Component.java @@ -17,7 +17,6 @@ import com.vaadin.terminal.Sizeable; import com.vaadin.terminal.VariableOwner; import com.vaadin.terminal.gwt.client.ComponentState; import com.vaadin.terminal.gwt.server.ClientConnector; -import com.vaadin.terminal.gwt.server.RpcTarget; /** * {@code Component} is the top-level interface that is and must be implemented @@ -52,8 +51,7 @@ import com.vaadin.terminal.gwt.server.RpcTarget; * @VERSION@ * @since 3.0 */ -public interface Component extends ClientConnector, Sizeable, Serializable, - RpcTarget { +public interface Component extends ClientConnector, Sizeable, Serializable { /** * Gets all user-defined CSS style names of a component. If the component diff --git a/src/com/vaadin/ui/CssLayout.java b/src/com/vaadin/ui/CssLayout.java index ac4f4b31a9..0a2656af31 100644 --- a/src/com/vaadin/ui/CssLayout.java +++ b/src/com/vaadin/ui/CssLayout.java @@ -11,7 +11,7 @@ import com.vaadin.event.LayoutEvents.LayoutClickListener; import com.vaadin.event.LayoutEvents.LayoutClickNotifier; import com.vaadin.terminal.gwt.client.Connector; import com.vaadin.terminal.gwt.client.MouseEventDetails; -import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutServerRPC; +import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutServerRpc; import com.vaadin.terminal.gwt.client.ui.csslayout.CssLayoutState; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; @@ -58,7 +58,7 @@ import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; */ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { - private CssLayoutServerRPC rpc = new CssLayoutServerRPC() { + private CssLayoutServerRpc rpc = new CssLayoutServerRpc() { public void layoutClick(MouseEventDetails mouseDetails, Connector clickedConnector) { diff --git a/src/com/vaadin/ui/CustomLayout.java b/src/com/vaadin/ui/CustomLayout.java index 0d74fe9878..97cea1c49d 100644 --- a/src/com/vaadin/ui/CustomLayout.java +++ b/src/com/vaadin/ui/CustomLayout.java @@ -9,8 +9,14 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import com.vaadin.terminal.PaintException; +import com.vaadin.terminal.PaintTarget; +import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.gwt.client.ui.customlayout.CustomLayoutState; +import com.vaadin.terminal.gwt.server.JsonPaintTarget; /** * <p> @@ -42,7 +48,7 @@ import com.vaadin.terminal.gwt.client.ui.customlayout.CustomLayoutState; * @since 3.0 */ @SuppressWarnings("serial") -public class CustomLayout extends AbstractLayout { +public class CustomLayout extends AbstractLayout implements Vaadin6Component { private static final int BUFFER_SIZE = 10000; @@ -299,4 +305,20 @@ public class CustomLayout extends AbstractLayout { "CustomLayout does not support margins."); } + public void changeVariables(Object source, Map<String, Object> variables) { + // Nothing to see here + } + + public void paintContent(PaintTarget target) throws PaintException { + // Workaround to make the CommunicationManager read the template file + // and send it to the client + String templateName = getState().getTemplateName(); + if (templateName != null && templateName.length() != 0) { + Set<Object> usedResources = ((JsonPaintTarget) target) + .getUsedResources(); + String resourceName = "layouts/" + templateName + ".html"; + usedResources.add(resourceName); + } + } + } diff --git a/src/com/vaadin/ui/Embedded.java b/src/com/vaadin/ui/Embedded.java index 052436cef7..1bcd984666 100644 --- a/src/com/vaadin/ui/Embedded.java +++ b/src/com/vaadin/ui/Embedded.java @@ -17,7 +17,7 @@ import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.ClickEventHandler; import com.vaadin.terminal.gwt.client.ui.embedded.EmbeddedConnector; -import com.vaadin.terminal.gwt.client.ui.embedded.EmbeddedServerRPC; +import com.vaadin.terminal.gwt.client.ui.embedded.EmbeddedServerRpc; /** * Component for embedding external objects. @@ -80,7 +80,7 @@ public class Embedded extends AbstractComponent implements Vaadin6Component { private String altText; - private EmbeddedServerRPC rpc = new EmbeddedServerRPC() { + private EmbeddedServerRpc rpc = new EmbeddedServerRpc() { public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(Embedded.this, mouseDetails)); } diff --git a/src/com/vaadin/ui/GridLayout.java b/src/com/vaadin/ui/GridLayout.java index 689cdcf28e..0ab729ce5c 100644 --- a/src/com/vaadin/ui/GridLayout.java +++ b/src/com/vaadin/ui/GridLayout.java @@ -22,7 +22,7 @@ import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.gwt.client.Connector; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.LayoutClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.gridlayout.GridLayoutServerRPC; +import com.vaadin.terminal.gwt.client.ui.gridlayout.GridLayoutServerRpc; import com.vaadin.terminal.gwt.client.ui.gridlayout.GridLayoutState; /** @@ -56,7 +56,7 @@ public class GridLayout extends AbstractLayout implements Layout.AlignmentHandler, Layout.SpacingHandler, LayoutClickNotifier, Vaadin6Component { - private GridLayoutServerRPC rpc = new GridLayoutServerRPC() { + private GridLayoutServerRpc rpc = new GridLayoutServerRpc() { public void layoutClick(MouseEventDetails mouseDetails, Connector clickedConnector) { diff --git a/src/com/vaadin/ui/Panel.java b/src/com/vaadin/ui/Panel.java index e358462bbb..b2916f78c7 100644 --- a/src/com/vaadin/ui/Panel.java +++ b/src/com/vaadin/ui/Panel.java @@ -18,7 +18,7 @@ import com.vaadin.terminal.Scrollable; import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.ClickEventHandler; -import com.vaadin.terminal.gwt.client.ui.panel.PanelServerRPC; +import com.vaadin.terminal.gwt.client.ui.panel.PanelServerRpc; import com.vaadin.terminal.gwt.client.ui.panel.PanelState; import com.vaadin.ui.Component.Focusable; @@ -47,7 +47,7 @@ public class Panel extends AbstractComponentContainer implements Scrollable, */ protected ActionManager actionManager; - private PanelServerRPC rpc = new PanelServerRPC() { + private PanelServerRpc rpc = new PanelServerRpc() { public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(Panel.this, mouseDetails)); } diff --git a/src/com/vaadin/ui/Root.java b/src/com/vaadin/ui/Root.java index 541127e092..405ae8da93 100644 --- a/src/com/vaadin/ui/Root.java +++ b/src/com/vaadin/ui/Root.java @@ -33,7 +33,7 @@ import com.vaadin.terminal.WrappedRequest.BrowserDetails; import com.vaadin.terminal.gwt.client.ComponentState; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.notification.VNotification; -import com.vaadin.terminal.gwt.client.ui.root.RootServerRPC; +import com.vaadin.terminal.gwt.client.ui.root.RootServerRpc; import com.vaadin.terminal.gwt.client.ui.root.RootState; import com.vaadin.terminal.gwt.client.ui.root.VRoot; import com.vaadin.tools.ReflectTools; @@ -409,7 +409,7 @@ public abstract class Root extends AbstractComponentContainer implements private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker( this); - private RootServerRPC rpc = new RootServerRPC() { + private RootServerRpc rpc = new RootServerRpc() { public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(Root.this, mouseDetails)); } diff --git a/src/com/vaadin/ui/Table.java b/src/com/vaadin/ui/Table.java index 2fffedd9d6..ba4c6529c5 100644 --- a/src/com/vaadin/ui/Table.java +++ b/src/com/vaadin/ui/Table.java @@ -1930,7 +1930,7 @@ public class Table extends AbstractSelect implements Action.Container, if (index < firstIndexNotInCache && index >= pageBufferFirstIndex && pageBuffer[CELL_GENERATED_ROW][indexInOldBuffer] == null - && pageBuffer[CELL_ITEMID][indexInOldBuffer] == id) { + && id.equals(pageBuffer[CELL_ITEMID][indexInOldBuffer])) { // we already have data in our cache, // recycle it instead of fetching it via // getValue/getPropertyValue diff --git a/src/com/vaadin/ui/Window.java b/src/com/vaadin/ui/Window.java index 6c3d75a920..3c17baf414 100644 --- a/src/com/vaadin/ui/Window.java +++ b/src/com/vaadin/ui/Window.java @@ -24,7 +24,7 @@ import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Vaadin6Component; import com.vaadin.terminal.gwt.client.MouseEventDetails; -import com.vaadin.terminal.gwt.client.ui.window.WindowServerRPC; +import com.vaadin.terminal.gwt.client.ui.window.WindowServerRpc; import com.vaadin.terminal.gwt.client.ui.window.WindowState; /** @@ -76,7 +76,7 @@ import com.vaadin.terminal.gwt.client.ui.window.WindowState; public class Window extends Panel implements FocusNotifier, BlurNotifier, Vaadin6Component { - private WindowServerRPC rpc = new WindowServerRPC() { + private WindowServerRpc rpc = new WindowServerRpc() { public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(Window.this, mouseDetails)); diff --git a/tests/client-side/com/vaadin/terminal/gwt/client/TestVBrowserDetailsUserAgentParser.java b/tests/client-side/com/vaadin/terminal/gwt/client/TestVBrowserDetailsUserAgentParser.java index f661b6cf15..fedce98ecf 100644 --- a/tests/client-side/com/vaadin/terminal/gwt/client/TestVBrowserDetailsUserAgentParser.java +++ b/tests/client-side/com/vaadin/terminal/gwt/client/TestVBrowserDetailsUserAgentParser.java @@ -2,8 +2,6 @@ package com.vaadin.terminal.gwt.client; import junit.framework.TestCase; -import com.vaadin.terminal.gwt.client.VBrowserDetails; - public class TestVBrowserDetailsUserAgentParser extends TestCase { private static final String FIREFOX30_WINDOWS = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6"; @@ -37,6 +35,15 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { private static final String SAFARI3_WINDOWS = "Mozilla/5.0 (Windows; U; Windows NT 5.1; cs-CZ) AppleWebKit/525.28.3 (KHTML, like Gecko) Version/3.2.3 Safari/525.29"; private static final String SAFARI4_MAC = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; en-us) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7"; + private static final String IPHONE_IOS_5_1 = "Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B179 Safari/7534.48.3"; + private static final String IPHONE_IOS_4_0 = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7"; + private static final String IPAD_IOS_4_3_1 = "Mozilla/5.0 (iPad; U; CPU OS 4_3_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8G4 Safari/6533.18.5"; + + private static final String ANDROID_HTC_2_1 = "Mozilla/5.0 (Linux; U; Android 2.1-update1; en-us; ADR6300 Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17"; + private static final String ANDROID_GOOGLE_NEXUS_2_2 = "Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"; + private static final String ANDROID_MOTOROLA_3_0 = "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13"; + private static final String ANDROID_GALAXY_NEXUS_4_0_4_CHROME = "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"; + public void testSafari3() { VBrowserDetails bd = new VBrowserDetails(SAFARI3_WINDOWS); assertWebKit(bd); @@ -57,6 +64,86 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { assertMacOSX(bd); } + public void testIPhoneIOS5() { + VBrowserDetails bd = new VBrowserDetails(IPHONE_IOS_5_1); + assertWebKit(bd); + assertSafari(bd); + assertBrowserMajorVersion(bd, 5); + assertBrowserMinorVersion(bd, 1); + assertEngineVersion(bd, 534f); + assertIOS(bd, 5, 1); + } + + public void testIPhoneIOS4() { + VBrowserDetails bd = new VBrowserDetails(IPHONE_IOS_4_0); + assertWebKit(bd); + assertSafari(bd); + assertBrowserMajorVersion(bd, 4); + assertBrowserMinorVersion(bd, 0); + assertEngineVersion(bd, 532f); + assertIOS(bd, 4, 0); + } + + public void testIPadIOS4() { + VBrowserDetails bd = new VBrowserDetails(IPAD_IOS_4_3_1); + assertWebKit(bd); + assertSafari(bd); + assertBrowserMajorVersion(bd, 5); + assertBrowserMinorVersion(bd, 0); + assertEngineVersion(bd, 533f); + assertIOS(bd, 4, 3); + } + + public void testAndroid21() { + VBrowserDetails bd = new VBrowserDetails(ANDROID_HTC_2_1); + assertWebKit(bd); + assertSafari(bd); + assertBrowserMajorVersion(bd, 4); + assertBrowserMinorVersion(bd, 0); + assertEngineVersion(bd, 530f); + assertAndroid(bd, 2, 1); + + } + + public void testAndroid22() { + VBrowserDetails bd = new VBrowserDetails(ANDROID_GOOGLE_NEXUS_2_2); + assertWebKit(bd); + assertSafari(bd); + assertBrowserMajorVersion(bd, 4); + assertBrowserMinorVersion(bd, 0); + assertEngineVersion(bd, 533f); + assertAndroid(bd, 2, 2); + } + + public void testAndroid30() { + VBrowserDetails bd = new VBrowserDetails(ANDROID_MOTOROLA_3_0); + assertWebKit(bd); + assertSafari(bd); + assertBrowserMajorVersion(bd, 4); + assertBrowserMinorVersion(bd, 0); + assertEngineVersion(bd, 534f); + assertAndroid(bd, 3, 0); + } + + public void testAndroid40Chrome() { + VBrowserDetails bd = new VBrowserDetails( + ANDROID_GALAXY_NEXUS_4_0_4_CHROME); + assertWebKit(bd); + assertChrome(bd); + assertBrowserMajorVersion(bd, 18); + assertBrowserMinorVersion(bd, 0); + assertEngineVersion(bd, 535f); + assertAndroid(bd, 4, 0); + } + + private void assertOSMajorVersion(VBrowserDetails bd, int i) { + assertEquals(i, bd.getOperatingSystemMajorVersion()); + } + + private void assertOSMinorVersion(VBrowserDetails bd, int i) { + assertEquals(i, bd.getOperatingSystemMinorVersion()); + } + public void testChrome3() { VBrowserDetails bd = new VBrowserDetails(CHROME3_MAC); assertWebKit(bd); @@ -352,18 +439,47 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { assertFalse(browserDetails.isLinux()); assertFalse(browserDetails.isWindows()); assertTrue(browserDetails.isMacOSX()); + assertFalse(browserDetails.isAndroid()); + } + + private void assertAndroid(VBrowserDetails browserDetails, + int majorVersion, int minorVersion) { + assertFalse(browserDetails.isLinux()); + assertFalse(browserDetails.isWindows()); + assertFalse(browserDetails.isMacOSX()); + assertFalse(browserDetails.isIOS()); + assertTrue(browserDetails.isAndroid()); + + assertOSMajorVersion(browserDetails, majorVersion); + assertOSMinorVersion(browserDetails, minorVersion); + } + + private void assertIOS(VBrowserDetails browserDetails, int majorVersion, + int minorVersion) { + assertFalse(browserDetails.isLinux()); + assertFalse(browserDetails.isWindows()); + assertFalse(browserDetails.isMacOSX()); + assertTrue(browserDetails.isIOS()); + assertFalse(browserDetails.isAndroid()); + + assertOSMajorVersion(browserDetails, majorVersion); + assertOSMinorVersion(browserDetails, minorVersion); } private void assertWindows(VBrowserDetails browserDetails) { assertFalse(browserDetails.isLinux()); assertTrue(browserDetails.isWindows()); assertFalse(browserDetails.isMacOSX()); + assertFalse(browserDetails.isIOS()); + assertFalse(browserDetails.isAndroid()); } private void assertLinux(VBrowserDetails browserDetails) { assertTrue(browserDetails.isLinux()); assertFalse(browserDetails.isWindows()); assertFalse(browserDetails.isMacOSX()); + assertFalse(browserDetails.isIOS()); + assertFalse(browserDetails.isAndroid()); } } diff --git a/tests/integration-testscripts/GateIn-3/integration-test-GateIn-3.1.0-portlet2.html b/tests/integration-testscripts/GateIn-3/integration-test-GateIn-3.1.0-portlet2.html index d97a9dce4a..85258d7036 100644 --- a/tests/integration-testscripts/GateIn-3/integration-test-GateIn-3.1.0-portlet2.html +++ b/tests/integration-testscripts/GateIn-3/integration-test-GateIn-3.1.0-portlet2.html @@ -118,7 +118,7 @@ </tr> <tr> <td>mouseClickAndWait</td> - <td>//div[@id='UIPage']/div/div/div[2]/div/div/div/div/div/div/div[2]/div/div[5]/div/div/a</td> + <td>//div[@id='UIPage']/div/div/div[2]/div/div/div/div/div/div/div[2]/div[5]/div/a</td> <td>10,10</td> </tr> <tr> @@ -138,7 +138,7 @@ </tr> <tr> <td>mouseClickAndWait</td> - <td>//div[@id='UIPage']/div/div/div[2]/div/div/div/div/div/div/div[2]/div/div[5]/div/div/a</td> + <td>//div[@id='UIPage']/div/div/div[2]/div/div/div/div/div/div/div[2]/div[5]/div/a</td> <td>15,8</td> </tr> <tr> diff --git a/tests/server-side/com/vaadin/tests/server/SourceFileChecker.java b/tests/server-side/com/vaadin/tests/server/SourceFileChecker.java index 453aab5af8..9906990165 100644 --- a/tests/server-side/com/vaadin/tests/server/SourceFileChecker.java +++ b/tests/server-side/com/vaadin/tests/server/SourceFileChecker.java @@ -5,6 +5,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.util.HashSet; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import junit.framework.Assert; import junit.framework.TestCase; @@ -81,6 +83,17 @@ public class SourceFileChecker extends TestCase { } } + public void testGwtFilesUsingEntry() { + Set<String> ignore = new HashSet<String>(alwaysIgnore); + ignore.add(externalJavaFiles); + validateFiles( + SRC_DIR, + new GwtEntryChecker(), + ignore, + "The following files might export javscript callbacks without $entry:\n{0}", + ".java"); + } + public interface FileValidator { void validateFile(File f) throws Exception; } @@ -171,4 +184,33 @@ public class SourceFileChecker extends TestCase { } } } + + class GwtEntryChecker extends FileContentsValidator { + // Matches e.g. + // @com.vaadin.terminal.gwt.client.HistoryImplIEVaadin::escapeHtml( + private final Matcher matcher = Pattern.compile("@[\\w.]+::\\w+\\(") + .matcher(""); + + @Override + protected void validateContents(File f, String contents) + throws Exception { + matcher.reset(contents); + while (matcher.find()) { + int start = matcher.start(); + + // Search backwards to find index of native block start + int nativeBlockStart = contents.lastIndexOf("/*-{", start); + + // Get contents between block start and our match + String beforeMatchInBlock = contents.substring( + nativeBlockStart, start); + + // Fail if there's no $entry + if (!beforeMatchInBlock.contains("$entry")) { + throw new IllegalArgumentException(); + } + } + } + + } } diff --git a/tests/server-side/com/vaadin/tests/server/component/tree/TestHasChildren.java b/tests/server-side/com/vaadin/tests/server/component/tree/TestHasChildren.java new file mode 100644 index 0000000000..66535d3ffb --- /dev/null +++ b/tests/server-side/com/vaadin/tests/server/component/tree/TestHasChildren.java @@ -0,0 +1,25 @@ +package com.vaadin.tests.server.component.tree; + +import junit.framework.TestCase; + +import com.vaadin.ui.Tree; + +public class TestHasChildren extends TestCase { + + private Tree tree; + + @Override + protected void setUp() { + tree = new Tree(); + tree.addItem("parent"); + tree.addItem("child"); + tree.setChildrenAllowed("parent", true); + tree.setParent("child", "parent"); + } + + public void testRemoveChildren() { + assertTrue(tree.hasChildren("parent")); + tree.removeItem("child"); + assertFalse(tree.hasChildren("parent")); + } +} diff --git a/tests/test.xml b/tests/test.xml index 28459c37e9..67e48ee137 100644 --- a/tests/test.xml +++ b/tests/test.xml @@ -11,7 +11,7 @@ <!-- Configuration --> <!-- ================================================================== --> <!-- Browsers to use for testing --> - <property name="browsers-windows" value="winxp-ie8,win7-ie9,winxp-firefox11,winxp-safari5,winxp-googlechrome18,winxp-opera11" /> + <property name="browsers-windows" value="winxp-ie8,win7-ie9,winxp-firefox12,winxp-safari5,winxp-googlechrome18,winxp-opera11" /> <property name="browsers-linux" value="linux-firefox3,linux-opera10,linux-googlechrome8" /> <property name="browsers-mac" value="osx-firefox3,osx-opera10,osx-googlechrome8,osx-safari4,osx-safari5" /> diff --git a/tests/testbench/com/vaadin/tests/browserfeatures/FullHeightScrollbar.html b/tests/testbench/com/vaadin/tests/browserfeatures/FullHeightScrollbar.html new file mode 100644 index 0000000000..37fee35746 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/browserfeatures/FullHeightScrollbar.html @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://192.168.2.41:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/statictestfiles/browserfeatures/fullHeightScrollbar.html</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>1-withScrolling</td> +</tr> +<tr> + <td>mouseClick</td> + <td>disableScrolling</td> + <td>34,7</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>2-withoutScrolling</td> +</tr> +<tr> + <td>mouseClick</td> + <td>triggerReflow</td> + <td>34,7</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>3-afterReflow</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/DisableEnableCascade.html b/tests/testbench/com/vaadin/tests/components/DisableEnableCascade.html new file mode 100644 index 0000000000..029da64754 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/DisableEnableCascade.html @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>DisableEnableCascade</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">DisableEnableCascade</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.DisableEnableCascade?restartApplication</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>panel-disabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[1]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>tabsheet-disabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[1]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>all-enabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[2]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[1]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>tabsheet-button-disabled</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[2]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsDisableEnableCascade::/VVerticalLayout[0]/VVerticalLayout[0]/VButton[1]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>all-enabled2</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/DisableEnableCascade.java b/tests/testbench/com/vaadin/tests/components/DisableEnableCascade.java new file mode 100644 index 0000000000..aa83ff7a42 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/DisableEnableCascade.java @@ -0,0 +1,90 @@ +package com.vaadin.tests.components; + +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Component; +import com.vaadin.ui.Panel; +import com.vaadin.ui.TabSheet; + +public class DisableEnableCascade extends TestBase { + + private Panel outerPanel; + private TabSheet innerTabsheet; + private Button button; + private Button enableDisablePanelButton; + private Button enableDisableTabSheetButton; + private Button enableDisableButtonButton; + + @Override + protected void setup() { + + outerPanel = new Panel("Outer panel, enabled"); + innerTabsheet = new TabSheet(); + innerTabsheet.setCaption("Inner Tabsheet, enabled"); + + button = new Button("Button, enabled"); + + outerPanel.setContent(innerTabsheet); + innerTabsheet.addTab(button, "Tab containing button"); + + addComponent(outerPanel); + + enableDisablePanelButton = new Button("Disable panel", + new ClickListener() { + + public void buttonClick(ClickEvent event) { + enableDisable(outerPanel, enableDisablePanelButton); + + } + }); + + enableDisableTabSheetButton = new Button("Disable TabSheet", + new ClickListener() { + + public void buttonClick(ClickEvent event) { + enableDisable(innerTabsheet, + enableDisableTabSheetButton); + + } + }); + + enableDisableButtonButton = new Button("Disable Button", + new ClickListener() { + + public void buttonClick(ClickEvent event) { + enableDisable(button, enableDisableButtonButton); + + } + }); + + addComponent(enableDisablePanelButton); + addComponent(enableDisableTabSheetButton); + addComponent(enableDisableButtonButton); + } + + protected void enableDisable(Component target, Button button) { + if (target.isEnabled()) { + target.setEnabled(false); + button.setCaption(button.getCaption().replace("Disable", "Enable")); + target.setCaption(target.getCaption() + .replace("enabled", "disabled")); + } else { + target.setEnabled(true); + button.setCaption(button.getCaption().replace("Enable", "Disable")); + target.setCaption(target.getCaption() + .replace("disabled", "enabled")); + } + } + + @Override + protected String getDescription() { + return "Tests the disable state is cascaded correctly to children. Disabling a parent should disabled its children aswell. The buttons only toggle the state of the target component."; + } + + @Override + protected Integer getTicketNumber() { + return 8507; + } + +} diff --git a/tests/testbench/com/vaadin/tests/components/TouchScrollables.java b/tests/testbench/com/vaadin/tests/components/TouchScrollables.java new file mode 100644 index 0000000000..053691e738 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/TouchScrollables.java @@ -0,0 +1,314 @@ +package com.vaadin.tests.components; + +import java.util.Collection; + +import com.vaadin.data.Item; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.event.Action; +import com.vaadin.event.Action.Handler; +import com.vaadin.event.DataBoundTransferable; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; +import com.vaadin.event.dd.acceptcriteria.SourceIs; +import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; +import com.vaadin.tests.util.Person; +import com.vaadin.tests.util.PersonContainer; +import com.vaadin.tests.util.TestUtils; +import com.vaadin.ui.AbstractSelect.AbstractSelectTargetDetails; +import com.vaadin.ui.Accordion; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.HorizontalSplitPanel; +import com.vaadin.ui.Label; +import com.vaadin.ui.Layout; +import com.vaadin.ui.Panel; +import com.vaadin.ui.TabSheet; +import com.vaadin.ui.Table; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.Window; + +public class TouchScrollables extends TestBase { + java.util.Random r = new java.util.Random(1); + + private TabSheet testSelector = new TabSheet(); + + @Override + public void setup() { + getLayout().addComponent(testSelector); + testSelector.setHeight("500px"); + + addTest(getPanelTest()); + addTest(getSimpleTableTest()); + addTest(getDDSortableTableTest()); + addTest(getTabSheetTest()); + addTest(getSplitPanelTest()); + addTest(getAccordionTest()); + addTest(getSubWindowTest()); + + TestUtils + .injectCSS( + getLayout().getRoot(), + "body * {-webkit-user-select: none;} .v-table-row-drag-middle .v-table-cell-content {" + + " background-color: inherit ; border-bottom: 1px solid cyan;" + + "}" + + ".v-table-row-drag-middle .v-table-cell-wrapper {" + + " margin-bottom: -1px;" + "}" + "" + + ); + } + + private Component getPanelTest() { + Layout cssLayout = new CssLayout(); + cssLayout.setCaption("Panel"); + + final Panel p = new Panel(); + p.setHeight("400px"); + Label l50 = null; + for (int i = 0; i < 100; i++) { + Label c = new Label("Label" + i); + p.addComponent(c); + if (i == 50) { + l50 = c; + } + } + + final Label l = l50; + Button button = new Button("Scroll to label 50", + new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + getLayout().getRoot().scrollIntoView(l); + } + }); + cssLayout.addComponent(button); + button = new Button("Scroll to 100px", new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + p.setScrollTop(100); + } + }); + cssLayout.addComponent(button); + cssLayout.addComponent(p); + return cssLayout; + } + + private Component getTabSheetTest() { + TabSheet ts = new TabSheet(); + ts.setCaption("Tabsheet"); + ts.setHeight("100%"); + ts.addTab(getBigComponent(), "Tab 1"); + ts.addTab(getBigComponent(), "Tab 2"); + return ts; + } + + private Component getSplitPanelTest() { + HorizontalSplitPanel sp = new HorizontalSplitPanel(); + sp.setCaption("Splitpanel"); + sp.addComponent(getBigComponent()); + sp.addComponent(getBigComponent()); + return sp; + } + + private Component getSimpleTableTest() { + CssLayout cssLayout = new CssLayout(); + final Table table = new Table(); + + Button button = new Button("Toggle lazyloading"); + button.addListener(new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + if (table.getCacheRate() == 100) { + table.setCacheRate(2); + table.setPageLength(15); + } else { + table.setCacheRate(100); + table.setHeight("400px"); + } + } + }); + cssLayout.addComponent(button); + + button = new Button("Toggle selectable"); + button.addListener(new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + table.setSelectable(!table.isSelectable()); + } + }); + cssLayout.addComponent(button); + + table.addContainerProperty("foo", String.class, "bar"); + table.setRowHeaderMode(Table.ROW_HEADER_MODE_INDEX); + for (int i = 0; i < 1000; i++) { + table.addItem(); + } + cssLayout.addComponent(table); + cssLayout.setCaption("Table"); + return cssLayout; + } + + private Component getAccordionTest() { + Accordion a = new Accordion(); + a.setCaption("Accordion"); + a.setHeight("100%"); + a.addTab(getBigComponent(), "Tab 1"); + a.addTab(getBigComponent(), "Tab 2"); + a.addTab(getBigComponent(), "Tab 3"); + return a; + } + + private Component getSubWindowTest() { + Button b = new Button("Open subwindow", new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + Window w = new Window("Subwindow"); + w.center(); + w.setHeight("200px"); + w.addComponent(getBigComponent()); + getMainWindow().addWindow(w); + } + }); + return b; + } + + private Component getDDSortableTableTest() { + final Table table; + table = new Table(); + table.setCaption("DD sortable table with context menus"); + // table.setWidth("100%"); + table.setPageLength(10); + table.setRowHeaderMode(Table.ROW_HEADER_MODE_ID); + table.setSelectable(true); + table.setMultiSelect(true); + + table.addActionHandler(new Handler() { + + Action[] actions = new Action[] { new Action("FOO"), + new Action("BAR"), new Action("CAR") }; + + public Action[] getActions(Object target, Object sender) { + return actions; + } + + public void handleAction(Action action, Object sender, Object target) { + getLayout().getRoot().showNotification(action.getCaption()); + + } + }); + + populateTable(table); + + /* + * Make table rows draggable + */ + table.setDragMode(Table.TableDragMode.ROW); + + table.setDropHandler(new DropHandler() { + // accept only drags from this table + AcceptCriterion crit = new SourceIs(table); + + public AcceptCriterion getAcceptCriterion() { + return crit; + } + + public void drop(DragAndDropEvent dropEvent) { + AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dropEvent + .getTargetDetails(); + DataBoundTransferable transferable = (DataBoundTransferable) dropEvent + .getTransferable(); + Object itemIdOver = dropTargetData.getItemIdOver(); + Object itemId = transferable.getItemId(); + if (itemId == null || itemIdOver == null + || itemId.equals(itemIdOver)) { + return; // no move happened + } + + // IndexedContainer goodies... (hint: don't use it in real apps) + IndexedContainer containerDataSource = (IndexedContainer) table + .getContainerDataSource(); + int newIndex = containerDataSource.indexOfId(itemIdOver) - 1; + if (dropTargetData.getDropLocation() != VerticalDropLocation.TOP) { + newIndex++; + } + if (newIndex < 0) { + newIndex = 0; + } + Object idAfter = containerDataSource.getIdByIndex(newIndex); + Collection<?> selections = (Collection<?>) table.getValue(); + if (selections != null && selections.contains(itemId)) { + // dragged a selected item, if multiple rows selected, drag + // them too (functionality similar to apple mail) + for (Object object : selections) { + moveAfter(containerDataSource, object, idAfter); + } + + } else { + // move just the dragged row, not considering selection at + // all + moveAfter(containerDataSource, itemId, idAfter); + } + + } + + private void moveAfter(IndexedContainer containerDataSource, + Object itemId, Object idAfter) { + try { + IndexedContainer clone = null; + clone = (IndexedContainer) containerDataSource.clone(); + containerDataSource.removeItem(itemId); + Item newItem = containerDataSource.addItemAfter(idAfter, + itemId); + Item item = clone.getItem(itemId); + for (Object propId : item.getItemPropertyIds()) { + newItem.getItemProperty(propId).setValue( + item.getItemProperty(propId).getValue()); + } + + // TODO Auto-generated method stub + } catch (CloneNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + }); + return table; + } + + private void populateTable(Table table) { + table.addContainerProperty("Name", String.class, ""); + table.addContainerProperty("Weight", Integer.class, 0); + + PersonContainer testData = PersonContainer.createWithTestData(); + + for (int i = 0; i < 40; i++) { + Item addItem = table.addItem("Item" + i); + Person p = testData.getIdByIndex(i); + addItem.getItemProperty("Name").setValue( + p.getFirstName() + " " + p.getLastName()); + addItem.getItemProperty("Weight").setValue(50 + r.nextInt(60)); + } + + } + + private void addTest(final Component t) { + testSelector.addComponent(t); + } + + private Component getBigComponent() { + Layout l = new VerticalLayout(); + for (int i = 0; i < 100; i++) { + Label c = new Label("Label" + i); + l.addComponent(c); + } + return l; + } + + @Override + protected String getDescription() { + return "Various components and setups suitable for testing scrolling on touch devices."; + } + + @Override + protected Integer getTicketNumber() { + return null; + } +} diff --git a/tests/testbench/com/vaadin/tests/components/button/ButtonHtml.html b/tests/testbench/com/vaadin/tests/components/button/ButtonHtml.html new file mode 100644 index 0000000000..2d58d0e2e1 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/button/ButtonHtml.html @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>ButtonHtml</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">ButtonHtml</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.button.ButtonHtml?restartApplication</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>initial</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonHtml::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>after_1_click</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonHtml::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>after_2_clicks</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/button/ButtonHtml.java b/tests/testbench/com/vaadin/tests/components/button/ButtonHtml.java new file mode 100644 index 0000000000..253de5b43c --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/button/ButtonHtml.java @@ -0,0 +1,39 @@ +package com.vaadin.tests.components.button; + +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; + +public class ButtonHtml extends TestBase { + + @Override + protected void setup() { + Button b = new Button("<b>Plain text button</b>"); + addComponent(b); + + b = new Button( + "<span style=\"color: red; font-weight: bold;\">HTML</span> button"); + b.setHtmlContentAllowed(true); + addComponent(b); + + final Button swapButton = new Button("<i>Swap button<i>"); + swapButton.addListener(new Button.ClickListener() { + + public void buttonClick(ClickEvent event) { + swapButton.setHtmlContentAllowed(!swapButton + .isHtmlContentAllowed()); + } + }); + addComponent(swapButton); + } + + @Override + protected String getDescription() { + return "Verify that Button HTML rendering works"; + } + + @Override + protected Integer getTicketNumber() { + return 8663; + } +} diff --git a/tests/testbench/com/vaadin/tests/components/combobox/GridLayoutComboBoxZoomOut.java b/tests/testbench/com/vaadin/tests/components/combobox/GridLayoutComboBoxZoomOut.java new file mode 100644 index 0000000000..37b0fe21a1 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/combobox/GridLayoutComboBoxZoomOut.java @@ -0,0 +1,62 @@ +package com.vaadin.tests.components.combobox; + +import com.vaadin.tests.components.AbstractTestCase; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Layout; +import com.vaadin.ui.Root; +import com.vaadin.ui.Select; + +@SuppressWarnings("serial") +public class GridLayoutComboBoxZoomOut extends AbstractTestCase { + + @Override + public void init() { + Root.LegacyWindow mainWindow = new Root.LegacyWindow( + "Gridlayoutbug Application"); + setMainWindow(mainWindow); + + Label description = new Label( + "Open this application in Chrome, zoom out (cmd + \"-\") and " + + "open the ComboBox for weird behaviour."); + mainWindow.addComponent(description); + + Layout formLayout = new GridLayout(2, 1); + // formLayout.setWidth("100%"); + formLayout.setWidth("1000px"); + + Select countryField = new ComboBox(); + countryField.addItem("Finland"); + countryField.addItem("Sweden"); + countryField.addItem("Canada"); + countryField.addItem("USA"); + countryField.setCaption("Country"); + countryField.setWidth("100%"); + formLayout.addComponent(countryField); + + Select statusField = new ComboBox(); + statusField.addItem("Available"); + statusField.addItem("On vacation"); + statusField.addItem("Busy"); + statusField.addItem("Left the building"); + statusField.setCaption("Status"); + statusField.setWidth("100%"); + formLayout.addComponent(statusField); + + mainWindow.addComponent(formLayout); + } + + @Override + protected String getDescription() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected Integer getTicketNumber() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTemplate.html b/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTemplate.html new file mode 100644 index 0000000000..b2806afe5c --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTemplate.html @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head profile="http://selenium-ide.openqa.org/profiles/test-case"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><link rel="selenium.base" href="" /><title>com.vaadin.tests.components.accordion.AccordionInactiveTabSize</title></head><body><table cellpadding="1" cellspacing="1" border="1"><thead><tr><td rowspan="1" colspan="3">com.vaadin.tests.components.accordion.AccordionInactiveTabSize</td></tr></thead><tbody><tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.customcomponent.CustomLayoutUsingTemplate?restartApplication</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>initial</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscustomcomponentCustomLayoutUsingTemplate::/VVerticalLayout[0]/VVerticalLayout[0]/VCustomLayout[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>with-text-field</td> +</tr> +</tbody></table></body></html>
\ No newline at end of file diff --git a/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTheme.html b/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTheme.html new file mode 100644 index 0000000000..954afb2adb --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTheme.html @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head profile="http://selenium-ide.openqa.org/profiles/test-case"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><link rel="selenium.base" href="" /><title>com.vaadin.tests.components.accordion.AccordionInactiveTabSize</title></head><body><table cellpadding="1" cellspacing="1" border="1"><thead><tr><td rowspan="1" colspan="3">com.vaadin.tests.components.accordion.AccordionInactiveTabSize</td></tr></thead><tbody><tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.customcomponent.CustomLayoutUsingTheme?restartApplication</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>initial</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscustomcomponentCustomLayoutUsingTheme::/VVerticalLayout[0]/VVerticalLayout[0]/VCustomLayout[0]/VVerticalLayout[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>label</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscustomcomponentCustomLayoutUsingTheme::/VVerticalLayout[0]/VVerticalLayout[0]/VCustomLayout[0]/VVerticalLayout[0]/VButton[1]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>button</td> +</tr> +</tbody></table></body></html>
\ No newline at end of file diff --git a/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTheme.java b/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTheme.java index e8df335c7d..6ea1d0a0c5 100644 --- a/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTheme.java +++ b/tests/testbench/com/vaadin/tests/components/customcomponent/CustomLayoutUsingTheme.java @@ -18,25 +18,26 @@ public class CustomLayoutUsingTheme extends TestBase implements ClickListener { @Override protected void setup() { setTheme("tests-tickets"); - layout = new CustomLayout("Ticket1775.html"); + layout = new CustomLayout("Ticket1775"); addComponent(layout); layout.addComponent(new TextField("Username"), "loginUser"); layout.addComponent(new TextField("Password"), "loginPassword"); layout.addComponent(new Button("Login"), "loginButton"); + layout.setWidth(null); VerticalLayout menu = new VerticalLayout(); - menu.addComponent(new Button("Set main to label", new ClickListener() { + menu.addComponent(new Button("Set body to label", new ClickListener() { public void buttonClick(ClickEvent event) { - layout.addComponent(new Label(LoremIpsum.get(200)), "main"); + layout.addComponent(new Label(LoremIpsum.get(200)), "body"); } })); - menu.addComponent(new Button("Set main to huge NativeButton", + menu.addComponent(new Button("Set body to huge NativeButton", new ClickListener() { public void buttonClick(ClickEvent event) { layout.addComponent(new NativeButton( - "This is it, the main!"), "main"); + "This is it, the body!"), "body"); } })); layout.addComponent(menu, "menu"); diff --git a/tests/testbench/com/vaadin/tests/components/datefield/InlineDateFields.java b/tests/testbench/com/vaadin/tests/components/datefield/InlineDateFields.java index de08477dd3..b8c5be57bf 100644 --- a/tests/testbench/com/vaadin/tests/components/datefield/InlineDateFields.java +++ b/tests/testbench/com/vaadin/tests/components/datefield/InlineDateFields.java @@ -25,6 +25,10 @@ public class InlineDateFields extends ComponentTestCase<InlineDateField> { @Override protected void initializeComponents() { + InlineDateField hidden = new InlineDateField(); + hidden.setVisible(false); // Used to break rest of layout #8693 + addComponent(hidden); + Locale locale = LOCALES[0]; InlineDateField pd = createInlineDateField("Undefined width", "-1", diff --git a/tests/testbench/com/vaadin/tests/components/datefield/InlineDateFieldsHiddenOnStart.html b/tests/testbench/com/vaadin/tests/components/datefield/InlineDateFieldsHiddenOnStart.html new file mode 100644 index 0000000000..6b79ef419b --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/datefield/InlineDateFieldsHiddenOnStart.html @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.InlineDateFields?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldInlineDateFields::PID_SLocale-en_US-undefined-wide/VCalendarPanel[0]#header</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/label/MarginsInLabels.html b/tests/testbench/com/vaadin/tests/components/label/MarginsInLabels.html new file mode 100644 index 0000000000..080200092d --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/label/MarginsInLabels.html @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head profile="http://selenium-ide.openqa.org/profiles/test-case"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><link rel="selenium.base" href="" /><title>com.vaadin.tests.components.accordion.AccordionInactiveTabSize</title></head><body><table cellpadding="1" cellspacing="1" border="1"><thead><tr><td rowspan="1" colspan="3">com.vaadin.tests.components.accordion.AccordionInactiveTabSize</td></tr></thead><tbody><tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.label.MarginsInLabels?restartApplication</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td></td> +</tr> +</tbody></table></body></html>
\ No newline at end of file diff --git a/tests/testbench/com/vaadin/tests/components/gridlayout/MarginsInLabels.java b/tests/testbench/com/vaadin/tests/components/label/MarginsInLabels.java index 1cbed2a17d..699f94bb7e 100644 --- a/tests/testbench/com/vaadin/tests/components/gridlayout/MarginsInLabels.java +++ b/tests/testbench/com/vaadin/tests/components/label/MarginsInLabels.java @@ -1,4 +1,4 @@ -package com.vaadin.tests.components.gridlayout; +package com.vaadin.tests.components.label; import com.vaadin.terminal.WrappedRequest; import com.vaadin.tests.components.AbstractTestRoot; diff --git a/tests/testbench/com/vaadin/tests/components/nativebutton/NativeButtonHtml.html b/tests/testbench/com/vaadin/tests/components/nativebutton/NativeButtonHtml.html new file mode 100644 index 0000000000..8a1b21c35d --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/nativebutton/NativeButtonHtml.html @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>NativeButtonHtml</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">NativeButtonHtml</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.nativebutton.NativeButtonHtml?restartApplication</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>initial</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsnativebuttonNativeButtonHtml::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VNativeButton[0]</td> + <td>116,9</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>after_1_click</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsnativebuttonNativeButtonHtml::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VNativeButton[0]</td> + <td>74,10</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>after_2_clicks</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/nativebutton/NativeButtonHtml.java b/tests/testbench/com/vaadin/tests/components/nativebutton/NativeButtonHtml.java new file mode 100644 index 0000000000..011439f810 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/nativebutton/NativeButtonHtml.java @@ -0,0 +1,41 @@ +package com.vaadin.tests.components.nativebutton; + +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.NativeButton; + +public class NativeButtonHtml extends TestBase { + + @Override + protected void setup() { + NativeButton b = new NativeButton("<b>Plain text button</b>"); + addComponent(b); + + b = new NativeButton( + "<span style=\"color: red; font-weight: bold;\">HTML</span> button"); + b.setHtmlContentAllowed(true); + addComponent(b); + + final NativeButton swapButton = new NativeButton("<i>Swap button<i>"); + swapButton.addListener(new Button.ClickListener() { + + public void buttonClick(ClickEvent event) { + swapButton.setHtmlContentAllowed(!swapButton + .isHtmlContentAllowed()); + } + }); + addComponent(swapButton); + } + + @Override + protected String getDescription() { + return "Verify that NativeButton HTML rendering works"; + } + + @Override + protected Integer getTicketNumber() { + // 8663 was for normal button (see ButtonHtml test) + return null; + } +} diff --git a/tests/testbench/com/vaadin/tests/components/table/TableScrollOnFocus.html b/tests/testbench/com/vaadin/tests/components/table/TableScrollOnFocus.html index 1a91211040..93e5a802ee 100644 --- a/tests/testbench/com/vaadin/tests/components/table/TableScrollOnFocus.html +++ b/tests/testbench/com/vaadin/tests/components/table/TableScrollOnFocus.html @@ -28,7 +28,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstableTableScrollOnFocus::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runcomvaadintestscomponentstableTableScrollOnFocus::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[1]</td> <td>391,141</td> </tr> <tr> diff --git a/tests/testbench/com/vaadin/tests/components/table/TableWithContainerRequiringEqualsForItemId.html b/tests/testbench/com/vaadin/tests/components/table/TableWithContainerRequiringEqualsForItemId.html new file mode 100644 index 0000000000..5364b1cd1e --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/table/TableWithContainerRequiringEqualsForItemId.html @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://arturwin.office.itmill.com:9999/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.table.TableWithContainerRequiringEqualsForItemId?debug&restartApplication</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[0]</td> + <td>523,81</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/FocusableScrollPanel[0]/VScrollTable$VScrollTableBody[0]/VScrollTable$VScrollTableBody$VScrollTableRow[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::PID_SLog_row_0</td> + <td>1. Button Button999 clicked</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/FocusableScrollPanel[0]/VScrollTable$VScrollTableBody[0]/VScrollTable$VScrollTableBody$VScrollTableRow[14]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::PID_SLog_row_0</td> + <td>2. Button Button985 clicked</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>19,7</td> +</tr> +<tr> + <td>contextmenu</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>0</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/FocusableScrollPanel[0]/VScrollTable$VScrollTableBody[0]/VScrollTable$VScrollTableBody$VScrollTableRow[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::PID_SLog_row_0</td> + <td>3. Button Button0 clicked</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>8,15</td> +</tr> +<tr> + <td>contextmenu</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>999</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/FocusableScrollPanel[0]/VScrollTable$VScrollTableBody[0]/VScrollTable$VScrollTableBody$VScrollTableRow[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentstableTableWithContainerRequiringEqualsForItemId::PID_SLog_row_0</td> + <td>4. Button Button999 clicked</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/table/TableWithContainerRequiringEqualsForItemId.java b/tests/testbench/com/vaadin/tests/components/table/TableWithContainerRequiringEqualsForItemId.java new file mode 100644 index 0000000000..1b0335b673 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/table/TableWithContainerRequiringEqualsForItemId.java @@ -0,0 +1,128 @@ +package com.vaadin.tests.components.table; + +import java.util.Date; + +import com.vaadin.data.util.BeanContainer; +import com.vaadin.data.util.BeanItem; +import com.vaadin.tests.components.TestBase; +import com.vaadin.tests.util.Log; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Component; +import com.vaadin.ui.Table; +import com.vaadin.ui.themes.Reindeer; + +public class TableWithContainerRequiringEqualsForItemId extends TestBase { + + private MyEntityContainer container = new MyEntityContainer(); + private Log log = new Log(10); + + public static class MyEntityContainer extends BeanContainer<Long, MyEntity> { + + public MyEntityContainer() { + super(MyEntity.class); + setBeanIdResolver(new BeanIdResolver<Long, TableWithContainerRequiringEqualsForItemId.MyEntity>() { + + public Long getIdForBean(MyEntity bean) { + // Return a new instance every time to ensure Table can + // handle it + return new Long(bean.getId()); + } + }); + + } + + @Override + public Long getIdByIndex(int index) { + // Explicitly get the id using the resolver to make sure the + // instance does not stay the same + BeanItem<MyEntity> beanItem = getItem(super.getIdByIndex(index)); + return getBeanIdResolver().getIdForBean(beanItem.getBean()); + }; + + } + + @Override + protected void setup() { + Table t = new Table("Table with 1000 item"); + t.addGeneratedColumn("Actions", new Table.ColumnGenerator() { + public Component generateCell(final Table source, + final Object itemId, final Object columnId) { + Button tripFolderLink = new Button("Button" + itemId); + tripFolderLink.addListener(new Button.ClickListener() { + public void buttonClick(final ClickEvent event) { + log.log("Button " + event.getButton().getCaption() + + " clicked"); + } + }); + tripFolderLink.setStyleName(Reindeer.BUTTON_SMALL); + return tripFolderLink; + } + }); + + for (int i = 0; i < 1000; i++) { + MyEntity myEntity = new MyEntity(i + "st"); + myEntity.setCreated(new Date(new Date().getTime() - 24 * 60 * 60 + * 1000L)); + myEntity.setId(i); + container.addBean(myEntity); + // entityProvider.addEntity(myEntity); + } + + t.setContainerDataSource(container); + t.setVisibleColumns(new String[] { "id", "created", "name", "Actions" }); + + addComponent(t); + addComponent(log); + + t.sort(new Object[] { "id" }, new boolean[] { false }); + + } + + @Override + protected String getDescription() { + return "Test that verifies that Table works correctly with containers which do not return the same instance of the itemId object but instead requires an itemId.equals(otherItemId) check"; + } + + @Override + protected Integer getTicketNumber() { + return 8712; + } + + public static class MyEntity { + + private long id; + + private String name; + + private Date created; + + public MyEntity() { + } + + public MyEntity(String string) { + name = string; + } + + public String getName() { + return name; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + } + +} diff --git a/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.html b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.html new file mode 100644 index 0000000000..ac06706aa5 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.html @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>TestCurrentPageFirstItem</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">TestCurrentPageFirstItem</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/TestCurrentPageFirstItem?restartApplication</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runTestCurrentPageFirstItem::/VHorizontalLayout[0]/ChildComponentContainer[4]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runTestCurrentPageFirstItem::/VHorizontalLayout[0]/ChildComponentContainer[5]/VButton[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runTestCurrentPageFirstItem::/VHorizontalLayout[0]/ChildComponentContainer[4]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td></td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java index 1748c27426..7fb096739a 100644 --- a/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java +++ b/tests/testbench/com/vaadin/tests/components/table/TestCurrentPageFirstItem.java @@ -1,60 +1,77 @@ package com.vaadin.tests.components.table; -import com.vaadin.Application; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.IndexedContainer; +import com.vaadin.tests.components.TestBase; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Root.LegacyWindow; +import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Table; import com.vaadin.ui.VerticalLayout; -public class TestCurrentPageFirstItem extends Application.LegacyApplication - implements ClickListener { +public class TestCurrentPageFirstItem extends TestBase implements ClickListener { private Button buttonIndex; private Button buttonItem; - private Table table; + private Table[] tables = new Table[4]; private int counter = 0; IndexedContainer container = new IndexedContainer(); @Override - public void init() { - try { - LegacyWindow main = new LegacyWindow("Table header Test"); - setMainWindow(main); - main.setSizeFull(); - // setTheme("testtheme"); - VerticalLayout baseLayout = new VerticalLayout(); - main.setContent(baseLayout); - - table = new Table(); - container.addContainerProperty("row", String.class, ""); - table.setContainerDataSource(container); - table.setWidth("100%"); - table.setPageLength(3); - buttonIndex = new Button("Add row and select last index", this); - buttonItem = new Button("Add row and select last item", this); - - baseLayout.addComponent(table); - baseLayout.addComponent(buttonIndex); - baseLayout.addComponent(buttonItem); - } catch (Exception e) { - e.printStackTrace(); + public void setup() { + container.addContainerProperty("row", String.class, ""); + + HorizontalLayout baseLayout = new HorizontalLayout(); + baseLayout.setHeight("115px"); + getMainWindow().setContent(baseLayout); + + for (int i = 0; i < tables.length; ++i) { + Table t = new Table(); + t.setContainerDataSource(container); + t.setWidth("100px"); + baseLayout.addComponent(t); + tables[i] = t; } + tables[0].setSizeFull(); + tables[0].setCaption("Full"); + tables[1].setHeight("100px"); + tables[1].setCaption("100px"); + tables[2].setHeight("95%"); + tables[2].setCaption("95%"); + tables[3].setPageLength(3); + tables[3].setCaption("3 rows"); + + buttonIndex = new Button("Add row and select last index", this); + buttonItem = new Button("Add row and select last item", this); + baseLayout.addComponent(buttonIndex); + baseLayout.addComponent(buttonItem); } public void buttonClick(ClickEvent event) { Item item = container.addItem(++counter); item.getItemProperty("row").setValue(counter + ""); - table.select(counter); - if (event.getButton() == buttonIndex) { - table.setCurrentPageFirstItemIndex(((Container.Indexed) table - .getContainerDataSource()).indexOfId(counter)); - } else { - table.setCurrentPageFirstItemId(counter); + for (int i = 0; i < tables.length; ++i) { + Table t = tables[i]; + t.select(counter); + if (event.getButton() == buttonIndex) { + t.setCurrentPageFirstItemIndex(((Container.Indexed) t + .getContainerDataSource()).indexOfId(counter)); + } else { + t.setCurrentPageFirstItemId(counter); + } } } + + @Override + protected String getDescription() { + return "Table height changes when using setCurrentPageFirstItemId"; + } + + @Override + protected Integer getTicketNumber() { + return 2864; + } } diff --git a/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.html b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.html new file mode 100644 index 0000000000..f5579c9875 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.html @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>ScrollbarsInNestedTabsheets</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">ScrollbarsInNestedTabsheets</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/ScrollbarsInNestedTabsheets?restartApplication</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runScrollbarsInNestedTabsheets::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VTabsheet[0]/VTabsheetPanel[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]</td> + <td>27,8</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>should-not-have-scrollbars</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.java b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.java new file mode 100644 index 0000000000..de250539ff --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/tabsheet/ScrollbarsInNestedTabsheets.java @@ -0,0 +1,58 @@ +package com.vaadin.tests.components.tabsheet; + +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.DateField; +import com.vaadin.ui.Label; +import com.vaadin.ui.Layout; +import com.vaadin.ui.TabSheet; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +@SuppressWarnings("serial") +public class ScrollbarsInNestedTabsheets extends TestBase { + + @Override + public void setup() { + setTheme("chameleon"); + final Label l = new Label("Select Sub Tab 2"); + final TabSheet t = new TabSheet(); + final TabSheet t2 = getTabSheet(); + t.addTab(t2, "Main Tab"); + addComponent(l); + addComponent(t); + } + + private TabSheet getTabSheet() { + final TabSheet t = new TabSheet(); + t.addTab(getDummyLayout1(), "Sub Tab 1"); + t.addTab(getDummyLayout2(), "Sub Tab 2"); + + return t; + } + + private Layout getDummyLayout1() { + final VerticalLayout l = new VerticalLayout(); + l.addComponent(new DateField("Date")); + + return l; + } + + private Layout getDummyLayout2() { + final VerticalLayout l = new VerticalLayout(); + l.addComponent(new DateField("Date")); + l.addComponent(new TextField("TextField")); + + return l; + } + + @Override + protected String getDescription() { + return "Nested tabsheets show unwanted scrollbars with Chameleon theme when the inner tabsheet is resized"; + } + + @Override + protected Integer getTicketNumber() { + return 8625; + } + +}
\ No newline at end of file diff --git a/tests/testbench/com/vaadin/tests/components/window/CloseSubWindow.java b/tests/testbench/com/vaadin/tests/components/window/CloseSubWindow.java index 4cd3854d6f..bcfbc7f55a 100644 --- a/tests/testbench/com/vaadin/tests/components/window/CloseSubWindow.java +++ b/tests/testbench/com/vaadin/tests/components/window/CloseSubWindow.java @@ -37,7 +37,7 @@ public class CloseSubWindow extends TestBase { Button closeButton = new Button("Close"); closeButton.addListener(new ClickListener() { public void buttonClick(ClickEvent event) { - window.close(); + event.getButton().findAncestor(Window.class).close(); } }); window.addComponent(closeButton); diff --git a/tests/testbench/com/vaadin/tests/dd/NotPaintedAcceptSource.html b/tests/testbench/com/vaadin/tests/dd/NotPaintedAcceptSource.html new file mode 100644 index 0000000000..03a4830584 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/dd/NotPaintedAcceptSource.html @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.dd.NotPaintedAcceptSource?restartApplication</td> + <td></td> +</tr> +<!--Drag value 0 to target--> +<tr> + <td>drag</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VHorizontalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>50,9</td> +</tr> +<tr> + <td>drop</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VHorizontalLayout[0]/ChildComponentContainer[1]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>124,20</td> +</tr> +<!--Assert drag was successful--> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VHorizontalLayout[0]/ChildComponentContainer[1]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td> + <td>Source 1 value 0</td> +</tr> +<!--Swap source and verify it was properly painted--> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/VVerticalLayout[0]/VHorizontalLayout[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>Source 2</td> +</tr> +<!--Drag value 0 from source 2 and verify--> +<tr> + <td>drag</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VHorizontalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>19,8</td> +</tr> +<tr> + <td>drop</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VHorizontalLayout[0]/ChildComponentContainer[1]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>139,18</td> +</tr> +<tr> + <td>contextmenu</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VHorizontalLayout[0]/ChildComponentContainer[1]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsddNotPaintedAcceptSource::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VHorizontalLayout[0]/ChildComponentContainer[1]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[0]</td> + <td>Source 2 value 0</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/dd/NotPaintedAcceptSource.java b/tests/testbench/com/vaadin/tests/dd/NotPaintedAcceptSource.java new file mode 100644 index 0000000000..74774f09ab --- /dev/null +++ b/tests/testbench/com/vaadin/tests/dd/NotPaintedAcceptSource.java @@ -0,0 +1,91 @@ +package com.vaadin.tests.dd; + +import com.vaadin.data.Item; +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; +import com.vaadin.event.dd.acceptcriteria.SourceIs; +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.AbstractSelect.AbstractSelectTargetDetails; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Table; +import com.vaadin.ui.Table.TableDragMode; +import com.vaadin.ui.Table.TableTransferable; + +public class NotPaintedAcceptSource extends TestBase { + + @Override + protected void setup() { + final Table source1 = createTable("Source 1"); + final Table source2 = createTable("Source 2"); + final Table target = createTable("Target"); + + source1.setDragMode(TableDragMode.ROW); + source2.setDragMode(TableDragMode.ROW); + + target.setDropHandler(new DropHandler() { + public AcceptCriterion getAcceptCriterion() { + return new SourceIs(source1, source2); + } + + public void drop(DragAndDropEvent event) { + TableTransferable transferable = (TableTransferable) event + .getTransferable(); + Item item = transferable.getSourceComponent().getItem( + transferable.getItemId()); + Object value = item.getItemProperty("value").getValue(); + AbstractSelectTargetDetails targetDetails = (AbstractSelectTargetDetails) event + .getTargetDetails(); + Object targetItemId = targetDetails.getItemIdOver(); + Object addItemAfter = target.addItemAfter(targetItemId); + target.getItem(addItemAfter).getItemProperty("value") + .setValue(value); + transferable.getSourceComponent().removeItem( + transferable.getItemId()); + } + }); + + final HorizontalLayout horizontalLayout = new HorizontalLayout(); + horizontalLayout.setSpacing(true); + horizontalLayout.addComponent(source1); + horizontalLayout.addComponent(target); + + addComponent(horizontalLayout); + + addComponent(new Button("Swap sources", new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + if (source1.getParent() != null) { + horizontalLayout.replaceComponent(source1, source2); + } else { + horizontalLayout.replaceComponent(source2, source1); + } + target.requestRepaint(); + } + })); + + } + + private Table createTable(String caption) { + Table table = new Table(caption); + table.addContainerProperty("value", String.class, ""); + for (int i = 0; i < 10; i++) { + table.addItem(new Object[] { caption + " value " + i }, + Integer.valueOf(i)); + } + table.setWidth("300px"); + return table; + } + + @Override + protected String getDescription() { + return "Including a component in an accept criterion when the actual component is not included in a layout and thus not painted should still allow painting the component properly when it is added to a layout."; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(8730); + } + +} diff --git a/tests/testbench/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.html b/tests/testbench/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.html index 4bc409b881..8408aba9fd 100644 --- a/tests/testbench/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.html +++ b/tests/testbench/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.html @@ -13,7 +13,7 @@ </thead><tbody> <tr> <td>open</td> - <td>/run/com.vaadin.tests.layouts.layouttester.LayoutTesterApplication</td> + <td>/run/com.vaadin.tests.layouts.layouttester.LayoutTesterApplication?restartApplication</td> <td></td> </tr> <tr> |