aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss6
-rw-r--r--WebContent/VAADIN/vaadinBootstrap.js2
-rw-r--r--client/src/com/vaadin/client/ApplicationConnection.java123
-rw-r--r--client/src/com/vaadin/client/LayoutManager.java40
-rw-r--r--client/src/com/vaadin/client/VCaption.java14
-rw-r--r--client/src/com/vaadin/client/VErrorMessage.java3
-rw-r--r--client/src/com/vaadin/client/VTooltip.java68
-rw-r--r--client/src/com/vaadin/client/communication/AtmospherePushConnection.java18
-rw-r--r--client/src/com/vaadin/client/communication/PushConnection.java7
-rw-r--r--client/src/com/vaadin/client/ui/Icon.java13
-rw-r--r--client/src/com/vaadin/client/ui/VFilterSelect.java5
-rw-r--r--client/src/com/vaadin/client/ui/VLink.java4
-rw-r--r--client/src/com/vaadin/client/ui/VNotification.java77
-rw-r--r--client/src/com/vaadin/client/ui/VOverlay.java2
-rw-r--r--client/src/com/vaadin/client/ui/VScrollTable.java41
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheet.java191
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheetBase.java2
-rw-r--r--client/src/com/vaadin/client/ui/VTree.java17
-rw-r--r--client/src/com/vaadin/client/ui/VUI.java36
-rw-r--r--client/src/com/vaadin/client/ui/VWindow.java357
-rw-r--r--client/src/com/vaadin/client/ui/VWindowOverlay.java77
-rw-r--r--client/src/com/vaadin/client/ui/button/ButtonConnector.java3
-rw-r--r--client/src/com/vaadin/client/ui/dd/VHtml5File.java8
-rw-r--r--client/src/com/vaadin/client/ui/link/LinkConnector.java59
-rw-r--r--client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java2
-rw-r--r--client/src/com/vaadin/client/ui/table/TableConnector.java2
-rw-r--r--client/src/com/vaadin/client/ui/tree/TreeConnector.java5
-rw-r--r--client/src/com/vaadin/client/ui/ui/UIConnector.java9
-rw-r--r--client/src/com/vaadin/client/ui/window/WindowConnector.java10
-rwxr-xr-xscripts/automerge7.sh12
-rw-r--r--server/src/com/vaadin/annotations/Widgetset.java2
-rw-r--r--server/src/com/vaadin/data/Container.java54
-rw-r--r--server/src/com/vaadin/data/util/AbstractBeanContainer.java77
-rw-r--r--server/src/com/vaadin/data/util/AbstractInMemoryContainer.java151
-rw-r--r--server/src/com/vaadin/data/util/BeanItem.java21
-rw-r--r--server/src/com/vaadin/data/util/IndexedContainer.java7
-rw-r--r--server/src/com/vaadin/data/util/NestedMethodProperty.java59
-rw-r--r--server/src/com/vaadin/data/util/NestedPropertyDescriptor.java24
-rw-r--r--server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java7
-rw-r--r--server/src/com/vaadin/data/util/converter/StringToBigDecimalConverter.java60
-rw-r--r--server/src/com/vaadin/data/util/converter/StringToLongConverter.java (renamed from server/src/com/vaadin/data/util/converter/StringToNumberConverter.java)45
-rw-r--r--server/src/com/vaadin/data/validator/BeanValidator.java19
-rw-r--r--server/src/com/vaadin/server/BootstrapHandler.java13
-rw-r--r--server/src/com/vaadin/server/Page.java16
-rw-r--r--server/src/com/vaadin/server/ServiceDestroyEvent.java50
-rw-r--r--server/src/com/vaadin/server/ServiceDestroyListener.java39
-rw-r--r--server/src/com/vaadin/server/SynchronizedRequestHandler.java25
-rw-r--r--server/src/com/vaadin/server/SystemMessages.java12
-rw-r--r--server/src/com/vaadin/server/VaadinPortlet.java6
-rw-r--r--server/src/com/vaadin/server/VaadinService.java71
-rw-r--r--server/src/com/vaadin/server/VaadinServlet.java86
-rw-r--r--server/src/com/vaadin/server/VaadinSession.java75
-rw-r--r--server/src/com/vaadin/server/communication/FileUploadHandler.java25
-rw-r--r--server/src/com/vaadin/server/communication/HeartbeatHandler.java9
-rw-r--r--server/src/com/vaadin/server/communication/PushRequestHandler.java9
-rw-r--r--server/src/com/vaadin/server/communication/ServerRpcHandler.java213
-rw-r--r--server/src/com/vaadin/server/communication/UIInitHandler.java80
-rw-r--r--server/src/com/vaadin/server/communication/UidlRequestHandler.java8
-rw-r--r--server/src/com/vaadin/server/communication/UidlWriter.java4
-rw-r--r--server/src/com/vaadin/ui/Component.java6
-rw-r--r--server/src/com/vaadin/ui/ConnectorTracker.java135
-rw-r--r--server/src/com/vaadin/ui/DragAndDropWrapper.java2
-rw-r--r--server/src/com/vaadin/ui/Link.java84
-rw-r--r--server/src/com/vaadin/ui/Notification.java150
-rw-r--r--server/src/com/vaadin/ui/NotificationConfiguration.java269
-rw-r--r--server/src/com/vaadin/ui/TabSheet.java180
-rw-r--r--server/src/com/vaadin/ui/UI.java39
-rw-r--r--server/src/com/vaadin/ui/Window.java202
-rw-r--r--server/tests/src/com/vaadin/data/util/BeanContainerTest.java24
-rw-r--r--server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java208
-rw-r--r--server/tests/src/com/vaadin/data/util/NestedMethodPropertyTest.java31
-rw-r--r--server/tests/src/com/vaadin/data/util/PropertyDescriptorTest.java16
-rw-r--r--server/tests/src/com/vaadin/data/util/TestIndexedContainer.java113
-rw-r--r--server/tests/src/com/vaadin/server/VaadinSessionTest.java2
-rw-r--r--server/tests/src/com/vaadin/tests/data/bean/BeanToValidate.java13
-rw-r--r--server/tests/src/com/vaadin/tests/data/bean/PersonWithBeanValidationAnnotations.java3
-rw-r--r--server/tests/src/com/vaadin/tests/data/converter/TestStringToBigDecimalConverter.java53
-rw-r--r--server/tests/src/com/vaadin/tests/data/converter/TestStringToLongConverter.java68
-rw-r--r--server/tests/src/com/vaadin/tests/data/converter/TestStringToNumberConverter.java24
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java9
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java42
-rw-r--r--server/tests/src/com/vaadin/tests/server/validation/TestBeanValidation.java29
-rw-r--r--shared/ivy.xml2
-rw-r--r--shared/src/com/vaadin/shared/ApplicationConstants.java22
-rwxr-xr-xshared/src/com/vaadin/shared/Position.java7
-rw-r--r--shared/src/com/vaadin/shared/ui/link/LinkState.java6
-rw-r--r--shared/src/com/vaadin/shared/ui/tabsheet/TabsheetBaseConstants.java2
-rw-r--r--shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java137
-rw-r--r--shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java2
-rw-r--r--shared/src/com/vaadin/shared/ui/ui/PageState.java5
-rw-r--r--shared/src/com/vaadin/shared/ui/ui/UIState.java18
-rw-r--r--shared/src/com/vaadin/shared/ui/window/WindowState.java16
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java104
-rw-r--r--[-rwxr-xr-x]theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java0
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java200
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java20
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java44
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java7
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java90
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java22
-rw-r--r--theme-compiler/tests/resources/css/compass-import.css49
-rw-r--r--theme-compiler/tests/resources/scss/compass-test/compass-import.scss4
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/_compass.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass-import2.scss4
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/_css3.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/_typography.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/_utilities.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/css3/_border-radius.scss4
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/css3/_inline-block.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/css3/_opacity.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/typography/_links.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/typography/_lists.scss3
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/typography/_text.scss6
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_color.scss4
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_general.scss5
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_sprites.scss6
-rw-r--r--theme-compiler/tests/resources/scss/compass-test2/license-readme.txt26
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java18
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/CompassImports.java80
-rw-r--r--uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.html92
-rw-r--r--uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.java85
-rw-r--r--uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.html36
-rw-r--r--uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.java30
-rw-r--r--uitest/src/com/vaadin/tests/application/TerminalErrorNotification.html2
-rw-r--r--uitest/src/com/vaadin/tests/application/VaadinSessionAttribute.html2
-rw-r--r--uitest/src/com/vaadin/tests/components/ErrorMessages.html8
-rw-r--r--uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java1
-rw-r--r--uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html8
-rw-r--r--uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html5
-rw-r--r--uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html6
-rw-r--r--uitest/src/com/vaadin/tests/components/form/FormTooltips.html36
-rw-r--r--uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndText.java124
-rw-r--r--uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndTextTest.java94
-rw-r--r--uitest/src/com/vaadin/tests/components/notification/CloseErrorNotificationWithEscape.html4
-rw-r--r--uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.html107
-rw-r--r--uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java116
-rw-r--r--uitest/src/com/vaadin/tests/components/slider/SliderTooltip.html15
-rw-r--r--uitest/src/com/vaadin/tests/components/table/DoublesInTable.java4
-rw-r--r--uitest/src/com/vaadin/tests/components/table/TableDragColumnFloatingElementStyles.html124
-rw-r--r--uitest/src/com/vaadin/tests/components/table/TableItemDescriptionGeneratorTest.html114
-rw-r--r--uitest/src/com/vaadin/tests/components/table/TableRemovedQuicklySendsInvalidRpcCalls.java107
-rw-r--r--uitest/src/com/vaadin/tests/components/table/TableShouldNotEatValueChanges.html4
-rw-r--r--uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html3
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html50
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java86
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.html10
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java2
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabSheetTest.java2
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.html76
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.java77
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabsheetTooltip.html2
-rw-r--r--uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.html123
-rw-r--r--uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.java123
-rw-r--r--uitest/src/com/vaadin/tests/components/tree/SimpleTree.java13
-rw-r--r--uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html9
-rw-r--r--uitest/src/com/vaadin/tests/components/uitest/UIScrollTest.html2
-rw-r--r--uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html10
-rw-r--r--uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html10
-rw-r--r--uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html10
-rw-r--r--uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html10
-rw-r--r--uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html10
-rw-r--r--uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html2
-rw-r--r--uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.html136
-rw-r--r--uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java172
-rw-r--r--uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html6
-rw-r--r--uitest/src/com/vaadin/tests/components/window/TooltipInWindow.html9
-rw-r--r--uitest/src/com/vaadin/tests/components/window/WindowCaptionTest.html7
-rw-r--r--uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html57
-rw-r--r--uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html2
-rw-r--r--uitest/src/com/vaadin/tests/fieldgroup/FieldBinderWithBeanValidation.java7
-rw-r--r--uitest/src/com/vaadin/tests/fieldgroup/IntegerRangeValidator.html41
-rw-r--r--uitest/src/com/vaadin/tests/minitutorials/v7a1/FormatTableValue.java6
-rw-r--r--uitest/src/com/vaadin/tests/util/TestUtils.java15
173 files changed, 6358 insertions, 970 deletions
diff --git a/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss b/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss
index 1a799814c1..14def56ab5 100644
--- a/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss
+++ b/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss
@@ -106,6 +106,12 @@
.#{$primaryStyleName}-tabitem-selected .v-caption {
cursor: default;
}
+.#{$primaryStyleName}-tabitem-focus .v-captiontext {
+ text-decoration: underline;
+}
+.#{$primaryStyleName}-tabitem-selected.#{$primaryStyleName}-tabitem-focus .v-captiontext {
+ text-decoration: inherit;
+}
.#{$primaryStyleName}-content {
border: 1px solid #aaa;
/* Vertical borders are not supported, use v-tabsheet-tabcontainer and v-tabsheet-deco to present these borders */
diff --git a/WebContent/VAADIN/vaadinBootstrap.js b/WebContent/VAADIN/vaadinBootstrap.js
index b2995dd0bd..bab759b812 100644
--- a/WebContent/VAADIN/vaadinBootstrap.js
+++ b/WebContent/VAADIN/vaadinBootstrap.js
@@ -120,6 +120,8 @@
url += '&theme=' + encodeURIComponent(theme);
}
+ url += "&v-appId=" + appId;
+
var extraParams = getConfig('extraParams')
if (extraParams !== undefined) {
url += extraParams;
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index 4314602bc2..08c755ef79 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -49,6 +49,7 @@ import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.regexp.shared.MatchResult;
@@ -90,6 +91,7 @@ import com.vaadin.client.ui.ui.UIConnector;
import com.vaadin.client.ui.window.WindowConnector;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.JsonConstants;
import com.vaadin.shared.Version;
import com.vaadin.shared.communication.LegacyChangeVariablesInvocation;
import com.vaadin.shared.communication.MethodInvocation;
@@ -136,10 +138,6 @@ public class ApplicationConnection {
public static final String ERROR_CLASSNAME_EXT = "-error";
- public static final char VAR_BURST_SEPARATOR = '\u001d';
-
- public static final char VAR_ESCAPE_CHARACTER = '\u001b';
-
/**
* A string that, if found in a non-JSON response to a UIDL request, will
* cause the browser to refresh the page. If followed by a colon, optional
@@ -270,8 +268,6 @@ public class ApplicationConnection {
/** Event bus for communication events */
private EventBus eventBus = GWT.create(SimpleEventBus.class);
- private int lastResponseId = -1;
-
/**
* The communication handler methods are called at certain points during
* communication with the server. This allows for making add-ons that keep
@@ -490,6 +486,10 @@ public class ApplicationConnection {
// initial UIDL provided in DOM, continue as if returned by request
handleJSONText(jsonText, -1);
+
+ // Tooltip can't be created earlier because the necessary fields are
+ // not setup to add it in the correct place in the DOM
+ getVTooltip().showAssistive(new TooltipInfo(" "));
}
}
@@ -671,7 +671,7 @@ public class ApplicationConnection {
}-*/;
protected void repaintAll() {
- makeUidlRequest("", getRepaintAllParameters());
+ makeUidlRequest(new JSONArray(), getRepaintAllParameters());
}
/**
@@ -702,20 +702,25 @@ public class ApplicationConnection {
/**
* Makes an UIDL request to the server.
*
- * @param requestData
- * Data that is passed to the server.
+ * @param reqInvocations
+ * Data containing RPC invocations and all related information.
* @param extraParams
* Parameters that are added as GET parameters to the url.
* Contains key=value pairs joined by & characters or is empty if
* no parameters should be added. Should not start with any
* special character.
*/
- protected void makeUidlRequest(final String requestData,
+ protected void makeUidlRequest(final JSONArray reqInvocations,
final String extraParams) {
startRequest();
- // Security: double cookie submission pattern
- final String payload = getCsrfToken() + VAR_BURST_SEPARATOR
- + requestData;
+
+ JSONObject payload = new JSONObject();
+ payload.put(ApplicationConstants.CSRF_TOKEN, new JSONString(
+ getCsrfToken()));
+ payload.put(ApplicationConstants.RPC_INVOCATIONS, reqInvocations);
+ payload.put(ApplicationConstants.SERVER_SYNC_ID, new JSONNumber(
+ lastSeenServerSyncId));
+
VConsole.log("Making UIDL Request with params: " + payload);
String uri = translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
+ ApplicationConstants.UIDL_PATH + '/');
@@ -739,7 +744,7 @@ public class ApplicationConnection {
* @param payload
* The contents of the request to send
*/
- protected void doUidlRequest(final String uri, final String payload) {
+ protected void doUidlRequest(final String uri, final JSONObject payload) {
RequestCallback requestCallback = new RequestCallback() {
@Override
public void onError(Request request, Throwable exception) {
@@ -902,14 +907,14 @@ public class ApplicationConnection {
* @throws RequestException
* if the request could not be sent
*/
- protected void doAjaxRequest(String uri, String payload,
+ protected void doAjaxRequest(String uri, JSONObject payload,
RequestCallback requestCallback) throws RequestException {
RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
// TODO enable timeout
// rb.setTimeoutMillis(timeoutMillis);
// TODO this should be configurable
- rb.setHeader("Content-Type", "text/plain;charset=utf-8");
- rb.setRequestData(payload);
+ rb.setHeader("Content-Type", JsonConstants.JSON_CONTENT_TYPE);
+ rb.setRequestData(payload.toString());
rb.setCallback(requestCallback);
final Request request = rb.send();
@@ -974,6 +979,29 @@ public class ApplicationConnection {
*/
private ValueMap serverTimingInfo;
+ /**
+ * Holds the last seen response id given by the server.
+ * <p>
+ * The server generates a strictly increasing id for each response to each
+ * request from the client. This ID is then replayed back to the server on
+ * each request. This helps the server in knowing in what state the client
+ * is, and compare it to its own state. In short, it helps with concurrent
+ * changes between the client and server.
+ * <p>
+ * Initial value, i.e. no responses received from the server, is
+ * {@link #UNDEFINED_SYNC_ID} ({@value #UNDEFINED_SYNC_ID}). This happens
+ * between the bootstrap HTML being loaded and the first UI being rendered;
+ */
+ private int lastSeenServerSyncId = UNDEFINED_SYNC_ID;
+
+ /**
+ * The value of an undefined sync id.
+ * <p>
+ * This must be <code>-1</code>, because of the contract in
+ * {@link #getLastResponseId()}
+ */
+ private static final int UNDEFINED_SYNC_ID = -1;
+
static final int MAX_CSS_WAITS = 100;
protected void handleWhenCSSLoaded(final String jsonText,
@@ -1254,7 +1282,13 @@ public class ApplicationConnection {
* @return and id identifying the response
*/
public int getLastResponseId() {
- return lastResponseId;
+ /*
+ * The discrepancy between field name and getter name is simply historic
+ * - API can't be changed, but the field was repurposed in a more
+ * general, yet compatible, use. "Response id" was deemed unsuitable a
+ * name, so it was called "server sync id" instead.
+ */
+ return lastSeenServerSyncId;
}
protected void handleUIDLMessage(final Date start, final String jsonText,
@@ -1281,6 +1315,17 @@ public class ApplicationConnection {
VConsole.log("Handling message from server");
eventBus.fireEvent(new ResponseHandlingStartedEvent(this));
+ if (json.containsKey(ApplicationConstants.SERVER_SYNC_ID)) {
+ int syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID);
+
+ assert (lastSeenServerSyncId == UNDEFINED_SYNC_ID || syncId == lastSeenServerSyncId + 1) : "Newly retrieved server sync id was not exactly one larger than the previous one (new: "
+ + syncId + ", last seen: " + lastSeenServerSyncId + ")";
+
+ lastSeenServerSyncId = syncId;
+ } else {
+ VConsole.error("Server response didn't contain an id.");
+ }
+
// Handle redirect
if (json.containsKey("redirect")) {
String url = json.getValueMap("redirect").getString("url");
@@ -1289,8 +1334,6 @@ public class ApplicationConnection {
return;
}
- lastResponseId++;
-
final MultiStepDuration handleUIDLDuration = new MultiStepDuration();
// Get security key
@@ -2464,15 +2507,13 @@ public class ApplicationConnection {
*/
private void buildAndSendVariableBurst(
LinkedHashMap<String, MethodInvocation> pendingInvocations) {
- final StringBuffer req = new StringBuffer();
- while (!pendingInvocations.isEmpty()) {
+ JSONArray reqJson = new JSONArray();
+ if (!pendingInvocations.isEmpty()) {
if (ApplicationConfiguration.isDebugMode()) {
Util.logVariableBurst(this, pendingInvocations.values());
}
- JSONArray reqJson = new JSONArray();
-
for (MethodInvocation invocation : pendingInvocations.values()) {
JSONArray invocationJson = new JSONArray();
invocationJson.set(0,
@@ -2511,9 +2552,6 @@ public class ApplicationConnection {
reqJson.set(reqJson.size(), invocationJson);
}
- // escape burst separators (if any)
- req.append(escapeBurstContents(reqJson.toString()));
-
pendingInvocations.clear();
// Keep tag string short
lastInvocationTag = 0;
@@ -2537,7 +2575,7 @@ public class ApplicationConnection {
getConfiguration().setWidgetsetVersionSent();
}
- makeUidlRequest(req.toString(), extraParams);
+ makeUidlRequest(reqJson, extraParams);
}
private boolean isJavascriptRpc(MethodInvocation invocation) {
@@ -2781,35 +2819,6 @@ public class ApplicationConnection {
}
/**
- * Encode burst separator characters in a String for transport over the
- * network. This protects from separator injection attacks.
- *
- * @param value
- * to encode
- * @return encoded value
- */
- protected String escapeBurstContents(String value) {
- final StringBuilder result = new StringBuilder();
- for (int i = 0; i < value.length(); ++i) {
- char character = value.charAt(i);
- switch (character) {
- case VAR_ESCAPE_CHARACTER:
- // fall-through - escape character is duplicated
- case VAR_BURST_SEPARATOR:
- result.append(VAR_ESCAPE_CHARACTER);
- // encode as letters for easier reading
- result.append(((char) (character + 0x30)));
- break;
- default:
- // the char is not a special one - add it to the result as is
- result.append(character);
- break;
- }
- }
- return result.toString();
- }
-
- /**
* Does absolutely nothing. Replaced by {@link LayoutManager}.
*
* @param container
diff --git a/client/src/com/vaadin/client/LayoutManager.java b/client/src/com/vaadin/client/LayoutManager.java
index 1ced003146..5d27527793 100644
--- a/client/src/com/vaadin/client/LayoutManager.java
+++ b/client/src/com/vaadin/client/LayoutManager.java
@@ -1449,10 +1449,15 @@ 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.
+ * changed. This method should be used whenever the size of an individual
+ * component 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.
+ * <p>
+ * To set an entire component hierarchy to be measured, use
+ * {@link #setNeedsMeasureRecursively(ComponentConnector)} instead.
+ * <p>
+ * If there is no upcoming layout phase, a new layout phase is scheduled.
*
* @param component
* the component whose size might have changed.
@@ -1466,6 +1471,33 @@ public class LayoutManager {
}
}
+ /**
+ * Informs this LayoutManager that some sizes in a component hierarchy might
+ * have changed. This method should be used whenever the size of any child
+ * component might have changed from outside of Vaadin's normal update
+ * phase, e.g. when a CSS class name related to sizing has been changed.
+ * <p>
+ * To set a single component to be measured, use
+ * {@link #setNeedsMeasure(ComponentConnector)} instead.
+ * <p>
+ * If there is no upcoming layout phase, a new layout phase is scheduled.
+ *
+ * @since 7.2
+ * @param component
+ * the component at the root of the component hierarchy to
+ * measure
+ */
+ public void setNeedsMeasureRecursively(ComponentConnector component) {
+ setNeedsMeasure(component);
+
+ if (component instanceof HasComponentsConnector) {
+ HasComponentsConnector hasComponents = (HasComponentsConnector) component;
+ for (ComponentConnector child : hasComponents.getChildComponents()) {
+ setNeedsMeasureRecursively(child);
+ }
+ }
+ }
+
public void setEverythingNeedsMeasure() {
everythingNeedsMeasure = true;
}
diff --git a/client/src/com/vaadin/client/VCaption.java b/client/src/com/vaadin/client/VCaption.java
index d0338de4a1..d96e6ed653 100644
--- a/client/src/com/vaadin/client/VCaption.java
+++ b/client/src/com/vaadin/client/VCaption.java
@@ -42,6 +42,8 @@ public class VCaption extends HTML {
private Icon icon;
+ private String iconAltText = "";
+
private Element captionText;
private final ApplicationConnection client;
@@ -112,6 +114,8 @@ public class VCaption extends HTML {
if (null != owner) {
AriaHelper.bindCaption(owner.getWidget(), null);
+ AriaHelper.handleInputInvalid(owner.getWidget(), false);
+ AriaHelper.handleInputRequired(owner.getWidget(), false);
}
}
@@ -300,6 +304,14 @@ public class VCaption extends HTML {
@Deprecated
public boolean updateCaptionWithoutOwner(String caption, boolean disabled,
boolean hasDescription, boolean hasError, String iconURL) {
+ return updateCaptionWithoutOwner(caption, disabled, hasDescription,
+ hasError, iconURL, "");
+ }
+
+ @Deprecated
+ public boolean updateCaptionWithoutOwner(String caption, boolean disabled,
+ boolean hasDescription, boolean hasError, String iconURL,
+ String iconAltText) {
boolean wasPlacedAfterComponent = placedAfterComponent;
// Caption is placed after component unless there is some part which
@@ -332,7 +344,7 @@ public class VCaption extends HTML {
// Icon forces the caption to be above the component
placedAfterComponent = false;
- icon.setUri(iconURL);
+ icon.setUri(iconURL, iconAltText);
} else if (icon != null) {
// Remove existing
diff --git a/client/src/com/vaadin/client/VErrorMessage.java b/client/src/com/vaadin/client/VErrorMessage.java
index 2e42b98a05..a384b451dd 100644
--- a/client/src/com/vaadin/client/VErrorMessage.java
+++ b/client/src/com/vaadin/client/VErrorMessage.java
@@ -31,9 +31,6 @@ public class VErrorMessage extends FlowPanel {
public VErrorMessage() {
super();
setStyleName(CLASSNAME);
-
- // Needed for binding with WAI-ARIA attributes
- getElement().setId(DOM.createUniqueId());
}
/**
diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java
index 6191821988..e687712b9c 100644
--- a/client/src/com/vaadin/client/VTooltip.java
+++ b/client/src/com/vaadin/client/VTooltip.java
@@ -15,7 +15,8 @@
*/
package com.vaadin.client;
-import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.LiveValue;
+import com.google.gwt.aria.client.RelevantValue;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
@@ -36,12 +37,12 @@ import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.ui.VOverlay;
+import com.vaadin.client.ui.VWindowOverlay;
/**
* TODO open for extension
*/
-public class VTooltip extends VOverlay {
+public class VTooltip extends VWindowOverlay {
private static final String CLASSNAME = "v-tooltip";
private static final int MARGIN = 4;
public static final int TOOLTIP_EVENTS = Event.ONKEYDOWN
@@ -57,7 +58,6 @@ public class VTooltip extends VOverlay {
private boolean justClosed = false;
private String uniqueId = DOM.createUniqueId();
- private Element layoutElement;
private int maxWidth;
// Delays for the tooltip, configurable on the server side
@@ -80,17 +80,28 @@ public class VTooltip extends VOverlay {
setWidget(layout);
layout.add(em);
DOM.setElementProperty(description, "className", CLASSNAME + "-text");
-
- layoutElement = layout.getElement();
- DOM.appendChild(layoutElement, description);
+ DOM.appendChild(layout.getElement(), description);
setSinkShadowEvents(true);
- // Used to bind the tooltip to the owner for assistive devices
- layoutElement.setId(uniqueId);
+ // When a tooltip is shown, the content of the tooltip changes. With a
+ // tooltip being a live-area, this change is notified to a assistive
+ // device.
+ Roles.getTooltipRole().set(getElement());
+ Roles.getTooltipRole().setAriaLiveProperty(getElement(),
+ LiveValue.ASSERTIVE);
+ Roles.getTooltipRole().setAriaRelevantProperty(getElement(),
+ RelevantValue.ADDITIONS);
+ }
- description.setId(DOM.createUniqueId());
- Roles.getTooltipRole().set(layoutElement);
- Roles.getTooltipRole().setAriaHiddenState(layoutElement, true);
+ /**
+ * Show the tooltip with the provided info for assistive devices.
+ *
+ * @param info
+ * with the content of the tooltip
+ */
+ public void showAssistive(TooltipInfo info) {
+ updatePosition(null, true);
+ show(info);
}
/**
@@ -229,10 +240,11 @@ public class VTooltip extends VOverlay {
@Override
public void hide() {
- super.hide();
- Roles.getTooltipRole().setAriaHiddenState(layoutElement, true);
- Roles.getTooltipRole().removeAriaDescribedbyProperty(
- tooltipEventHandler.currentElement);
+ em.updateMessage("");
+ description.setInnerHTML("");
+
+ updatePosition(null, true);
+ setPopupPosition(tooltipEventMouseX, tooltipEventMouseY);
}
private int tooltipEventMouseX;
@@ -287,9 +299,9 @@ public class VTooltip extends VOverlay {
private com.google.gwt.dom.client.Element currentElement = null;
/**
- * Current element focused
+ * Marker for handling of tooltip through focus
*/
- private boolean currentIsFocused;
+ private boolean handledByFocus;
/**
* Current tooltip active
@@ -392,6 +404,7 @@ public class VTooltip extends VOverlay {
*/
@Override
public void onBlur(BlurEvent be) {
+ handledByFocus = false;
handleHideEvent();
}
@@ -401,7 +414,7 @@ public class VTooltip extends VOverlay {
.getEventTarget());
// We can ignore move event if it's handled by move or over already
- if (currentElement == element && currentIsFocused == isFocused) {
+ if (currentElement == element && handledByFocus == true) {
return;
}
@@ -409,8 +422,6 @@ public class VTooltip extends VOverlay {
if (!connectorAndTooltipFound) {
if (isShowing()) {
handleHideEvent();
- Roles.getButtonRole()
- .removeAriaDescribedbyProperty(element);
} else {
currentTooltipInfo = null;
}
@@ -419,17 +430,12 @@ public class VTooltip extends VOverlay {
if (isShowing()) {
replaceCurrentTooltip();
- Roles.getTooltipRole().removeAriaDescribedbyProperty(
- currentElement);
} else {
showTooltip();
}
-
- Roles.getTooltipRole().setAriaDescribedbyProperty(element,
- Id.of(uniqueId));
}
- currentIsFocused = isFocused;
+ handledByFocus = isFocused;
currentElement = element;
}
}
@@ -464,9 +470,11 @@ public class VTooltip extends VOverlay {
@Override
public void setPopupPositionAndShow(PositionCallback callback) {
- super.setPopupPositionAndShow(callback);
-
- Roles.getTooltipRole().setAriaHiddenState(layoutElement, false);
+ if (isAttached()) {
+ callback.setPosition(getOffsetWidth(), getOffsetHeight());
+ } else {
+ super.setPopupPositionAndShow(callback);
+ }
}
/**
diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
index 4bf12ca1f3..acca80f02b 100644
--- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
+++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.Command;
import com.vaadin.client.ApplicationConfiguration;
import com.vaadin.client.ApplicationConnection;
@@ -110,7 +111,7 @@ public class AtmospherePushConnection implements PushConnection {
private JavaScriptObject socket;
- private ArrayList<String> messageQueue = new ArrayList<String>();
+ private ArrayList<JSONObject> messageQueue = new ArrayList<JSONObject>();
private State state = State.CONNECT_PENDING;
@@ -191,14 +192,8 @@ public class AtmospherePushConnection implements PushConnection {
}
}
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.client.communication.PushConenction#push(java.lang.String)
- */
@Override
- public void push(String message) {
+ public void push(JSONObject message) {
switch (state) {
case CONNECT_PENDING:
assert isActive();
@@ -210,12 +205,13 @@ public class AtmospherePushConnection implements PushConnection {
VConsole.log("Sending push message: " + message);
if (transport.equals("websocket")) {
- FragmentedMessage fragmented = new FragmentedMessage(message);
+ FragmentedMessage fragmented = new FragmentedMessage(
+ message.toString());
while (fragmented.hasNextFragment()) {
doPush(socket, fragmented.getNextFragment());
}
} else {
- doPush(socket, message);
+ doPush(socket, message.toString());
}
break;
case DISCONNECT_PENDING:
@@ -254,7 +250,7 @@ public class AtmospherePushConnection implements PushConnection {
switch (state) {
case CONNECT_PENDING:
state = State.CONNECTED;
- for (String message : messageQueue) {
+ for (JSONObject message : messageQueue) {
push(message);
}
messageQueue.clear();
diff --git a/client/src/com/vaadin/client/communication/PushConnection.java b/client/src/com/vaadin/client/communication/PushConnection.java
index a7eba224be..ba79af9d2c 100644
--- a/client/src/com/vaadin/client/communication/PushConnection.java
+++ b/client/src/com/vaadin/client/communication/PushConnection.java
@@ -16,6 +16,7 @@
package com.vaadin.client.communication;
+import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.Command;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ApplicationConnection.CommunicationErrorHandler;
@@ -53,14 +54,14 @@ public interface PushConnection {
* replay those messages in the original order when the connection has been
* established.
*
- * @param message
- * the message to push
+ * @param payload
+ * the payload to push
* @throws IllegalStateException
* if this connection is not active
*
* @see #isActive()
*/
- public void push(String message);
+ public void push(JSONObject payload);
/**
* Checks whether this push connection is in a state where it can push
diff --git a/client/src/com/vaadin/client/ui/Icon.java b/client/src/com/vaadin/client/ui/Icon.java
index 9d5829c079..4a0e858798 100644
--- a/client/src/com/vaadin/client/ui/Icon.java
+++ b/client/src/com/vaadin/client/ui/Icon.java
@@ -34,11 +34,19 @@ public class Icon extends UIObject {
}
public Icon(ApplicationConnection client, String uidlUri) {
+ this(client, uidlUri, "");
+ }
+
+ public Icon(ApplicationConnection client, String uidlUri, String iconAltText) {
this(client);
- setUri(uidlUri);
+ setUri(uidlUri, iconAltText);
}
public void setUri(String uidlUri) {
+ setUri(uidlUri, "");
+ }
+
+ public void setUri(String uidlUri, String uidlAlt) {
if (!uidlUri.equals(myUri)) {
/*
* Start sinking onload events, widgets responsibility to react. We
@@ -51,6 +59,8 @@ public class Icon extends UIObject {
DOM.setElementProperty(getElement(), "src", uri);
myUri = uidlUri;
}
+
+ setAlternateText(uidlAlt);
}
/**
@@ -63,5 +73,4 @@ public class Icon extends UIObject {
getElement().setAttribute("alt",
alternateText == null ? "" : alternateText);
}
-
}
diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java
index 9b14fbbb37..7efb5b8867 100644
--- a/client/src/com/vaadin/client/ui/VFilterSelect.java
+++ b/client/src/com/vaadin/client/ui/VFilterSelect.java
@@ -1786,6 +1786,11 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
if (client.hasEventListeners(this, EventId.FOCUS)) {
client.updateVariable(paintableId, EventId.FOCUS, "", true);
}
+
+ ComponentConnector connector = ConnectorMap.get(client).getConnector(
+ this);
+ client.getVTooltip().showAssistive(
+ connector.getTooltipInfo(getElement()));
}
/**
diff --git a/client/src/com/vaadin/client/ui/VLink.java b/client/src/com/vaadin/client/ui/VLink.java
index fa4ee36bcf..064a012873 100644
--- a/client/src/com/vaadin/client/ui/VLink.java
+++ b/client/src/com/vaadin/client/ui/VLink.java
@@ -23,7 +23,6 @@ import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HTML;
-import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Util;
import com.vaadin.shared.ui.BorderStyle;
@@ -70,9 +69,6 @@ public class VLink extends HTML implements ClickHandler {
/** For internal use only. May be removed or replaced in the future. */
public Icon icon;
- /** For internal use only. May be removed or replaced in the future. */
- public ApplicationConnection client;
-
public VLink() {
super();
getElement().appendChild(anchor);
diff --git a/client/src/com/vaadin/client/ui/VNotification.java b/client/src/com/vaadin/client/ui/VNotification.java
index 7019394e3b..128de0a285 100644
--- a/client/src/com/vaadin/client/ui/VNotification.java
+++ b/client/src/com/vaadin/client/ui/VNotification.java
@@ -21,19 +21,25 @@ import java.util.Date;
import java.util.EventObject;
import java.util.Iterator;
+import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
+import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.Position;
+import com.vaadin.shared.ui.ui.NotificationConfigurationBean;
+import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role;
import com.vaadin.shared.ui.ui.UIConstants;
public class VNotification extends VOverlay {
@@ -46,6 +52,12 @@ public class VNotification extends VOverlay {
public static final Position BOTTOM_LEFT = Position.BOTTOM_LEFT;
public static final Position BOTTOM_RIGHT = Position.BOTTOM_RIGHT;
+ /**
+ * Position that is only accessible for assistive devices, invisible for
+ * visual users.
+ */
+ public static final Position ASSISTIVE = Position.ASSISTIVE;
+
public static final int DELAY_FOREVER = -1;
public static final int DELAY_NONE = 0;
@@ -149,15 +161,67 @@ public class VNotification extends VOverlay {
}
public void show(Widget widget, Position position, String style) {
- setWidget(widget);
+ NotificationConfigurationBean styleSetup = getUiState(style);
+ setWaiAriaRole(styleSetup);
+
+ FlowPanel panel = new FlowPanel();
+ if (styleSetup.hasAssistivePrefix()) {
+ panel.add(new Label(styleSetup.getAssistivePrefix()));
+ AriaHelper.setVisibleForAssistiveDevicesOnly(panel.getElement(),
+ true);
+ }
+
+ panel.add(widget);
+
+ if (styleSetup.hasAssistivePostfix()) {
+ panel.add(new Label(styleSetup.getAssistivePostfix()));
+ AriaHelper.setVisibleForAssistiveDevicesOnly(panel.getElement(),
+ true);
+ }
+ setWidget(panel);
show(position, style);
}
public void show(String html, Position position, String style) {
- setWidget(new HTML(html));
+ NotificationConfigurationBean styleSetup = getUiState(style);
+ String assistiveDeviceOnlyStyle = AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE;
+
+ setWaiAriaRole(styleSetup);
+
+ String type = "";
+ String usage = "";
+
+ if (styleSetup != null && styleSetup.hasAssistivePrefix()) {
+ type = "<span class='" + assistiveDeviceOnlyStyle + "'>"
+ + styleSetup.getAssistivePrefix() + "</span>";
+ }
+
+ if (styleSetup != null && styleSetup.hasAssistivePostfix()) {
+ usage = "<span class='" + assistiveDeviceOnlyStyle + "'>"
+ + styleSetup.getAssistivePostfix() + "</span>";
+ }
+
+ setWidget(new HTML(type + html + usage));
show(position, style);
}
+ private NotificationConfigurationBean getUiState(String style) {
+ NotificationConfigurationBean styleSetup = getApplicationConnection()
+ .getUIConnector().getState().notificationConfiguration.setup
+ .get(style);
+ return styleSetup;
+ }
+
+ private void setWaiAriaRole(NotificationConfigurationBean styleSetup) {
+ Roles.getAlertRole().set(getElement());
+
+ if (styleSetup != null && styleSetup.getAssistiveRole() != null) {
+ if (Role.STATUS == styleSetup.getAssistiveRole()) {
+ Roles.getStatusRole().set(getElement());
+ }
+ }
+ }
+
public void show(Position position, String style) {
setOpacity(getElement(), startOpacity);
if (style != null) {
@@ -257,6 +321,10 @@ public class VNotification extends VOverlay {
DOM.setStyleAttribute(el, "top", "");
DOM.setStyleAttribute(el, "bottom", "0px");
break;
+ case ASSISTIVE:
+ DOM.setStyleAttribute(el, "top", "-2000px");
+ DOM.setStyleAttribute(el, "left", "-2000px");
+ break;
default:
case MIDDLE_CENTER:
center();
@@ -377,7 +445,8 @@ public class VNotification extends VOverlay {
final String parsedUri = client
.translateVaadinUri(notification
.getStringAttribute(UIConstants.ATTRIBUTE_NOTIFICATION_ICON));
- html += "<img src=\"" + Util.escapeAttribute(parsedUri) + "\" />";
+ html += "<img src=\"" + Util.escapeAttribute(parsedUri)
+ + "\" alt=\"\" />";
}
if (notification
.hasAttribute(UIConstants.ATTRIBUTE_NOTIFICATION_CAPTION)) {
@@ -417,6 +486,8 @@ public class VNotification extends VOverlay {
public static VNotification createNotification(int delayMsec, Widget owner) {
final VNotification notification = GWT.create(VNotification.class);
+ notification.setWaiAriaRole(null);
+
notification.delayMsec = delayMsec;
if (BrowserInfo.get().isTouchDevice()) {
new Timer() {
diff --git a/client/src/com/vaadin/client/ui/VOverlay.java b/client/src/com/vaadin/client/ui/VOverlay.java
index 2f5df6d4f3..9f84c16020 100644
--- a/client/src/com/vaadin/client/ui/VOverlay.java
+++ b/client/src/com/vaadin/client/ui/VOverlay.java
@@ -172,7 +172,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
*
* See default theme 'shadow.css' for implementation example.
*/
- private static final String SHADOW_HTML = "<div class=\"top-left\"></div><div class=\"top\"></div><div class=\"top-right\"></div><div class=\"left\"></div><div class=\"center\"></div><div class=\"right\"></div><div class=\"bottom-left\"></div><div class=\"bottom\"></div><div class=\"bottom-right\"></div>";
+ private static final String SHADOW_HTML = "<div aria-hidden=\"true\" class=\"top-left\"></div><div class=\"top\"></div><div class=\"top-right\"></div><div class=\"left\"></div><div class=\"center\"></div><div class=\"right\"></div><div class=\"bottom-left\"></div><div class=\"bottom\"></div><div class=\"bottom-right\"></div>";
/**
* Matches {@link PopupPanel}.ANIMATION_DURATION
diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java
index 0304acd590..02ae01d844 100644
--- a/client/src/com/vaadin/client/ui/VScrollTable.java
+++ b/client/src/com/vaadin/client/ui/VScrollTable.java
@@ -71,7 +71,6 @@ import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PopupPanel;
-import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
@@ -80,6 +79,7 @@ import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.MouseEventDetailsBuilder;
+import com.vaadin.client.StyleConstants;
import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
@@ -2274,7 +2274,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private int reqFirstRow = 0;
private int reqRows = 0;
- private boolean isRunning = false;
+ private boolean isRequestHandlerRunning = false;
public void triggerRowFetch(int first, int rows) {
setReqFirstRow(first);
@@ -2292,12 +2292,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
deferRowFetch(250);
}
- public boolean isRunning() {
- return isRunning;
+ public boolean isRequestHandlerRunning() {
+ return isRequestHandlerRunning;
}
public void deferRowFetch(int msec) {
- isRunning = true;
+ isRequestHandlerRunning = true;
if (reqRows > 0 && reqFirstRow < totalRows) {
schedule(msec);
@@ -2439,7 +2439,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
unSyncedselectionsBeforeRowFetch = new HashSet<Object>(
selectedRowKeys);
}
- isRunning = false;
+ isRequestHandlerRunning = false;
}
}
@@ -2447,7 +2447,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
* Sends request to refresh content at this position.
*/
public void refreshContent() {
- isRunning = true;
+ isRequestHandlerRunning = true;
int first = (int) (firstRowInViewPort - pageLength * cache_rate);
int reqRows = (int) (2 * pageLength * cache_rate + pageLength);
if (first < 0) {
@@ -2771,13 +2771,27 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td));
floatingCopyOfHeaderCell = DOM
.getChild(floatingCopyOfHeaderCell, 2);
- DOM.setElementProperty(floatingCopyOfHeaderCell, "className",
- VScrollTable.this.getStylePrimaryName() + "-header-drag");
+ // #12714 the shown "ghost element" should be inside
+ // v-overlay-container, and it should contain the same styles as the
+ // table to enable theming (except v-table & v-widget).
+ String stylePrimaryName = VScrollTable.this.getStylePrimaryName();
+ StringBuilder sb = new StringBuilder();
+ for (String s : VScrollTable.this.getStyleName().split(" ")) {
+ if (!s.equals(StyleConstants.UI_WIDGET)) {
+ sb.append(s);
+ if (s.equals(stylePrimaryName)) {
+ sb.append("-header-drag ");
+ } else {
+ sb.append(" ");
+ }
+ }
+ }
+ floatingCopyOfHeaderCell.setClassName(sb.toString().trim());
// otherwise might wrap or be cut if narrow column
DOM.setStyleAttribute(floatingCopyOfHeaderCell, "width", "auto");
updateFloatingCopysPosition(DOM.getAbsoluteLeft(td),
DOM.getAbsoluteTop(td));
- DOM.appendChild(RootPanel.get().getElement(),
+ DOM.appendChild(VOverlay.getOverlayContainer(client),
floatingCopyOfHeaderCell);
}
@@ -2792,8 +2806,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
}
private void hideFloatingCopy() {
- DOM.removeChild(RootPanel.get().getElement(),
- floatingCopyOfHeaderCell);
+ floatingCopyOfHeaderCell.removeFromParent();
floatingCopyOfHeaderCell = null;
}
@@ -7093,7 +7106,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private void deEmphasis() {
UIObject.setStyleName(getElement(),
- VScrollTable.this.getStylePrimaryName() + "-drag", false);
+ getStylePrimaryName() + "-drag", false);
if (lastEmphasized == null) {
return;
}
@@ -7119,7 +7132,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
private void emphasis(TableDDDetails details) {
deEmphasis();
UIObject.setStyleName(getElement(),
- VScrollTable.this.getStylePrimaryName() + "-drag", true);
+ getStylePrimaryName() + "-drag", true);
// iterate old and new emphasized row
for (Widget w : scrollBody.renderedRows) {
VScrollTableRow row = (VScrollTableRow) w;
diff --git a/client/src/com/vaadin/client/ui/VTabsheet.java b/client/src/com/vaadin/client/ui/VTabsheet.java
index 7dec62bde0..04cd9c09ba 100644
--- a/client/src/com/vaadin/client/ui/VTabsheet.java
+++ b/client/src/com/vaadin/client/ui/VTabsheet.java
@@ -19,6 +19,10 @@ package com.vaadin.client.ui;
import java.util.Iterator;
import java.util.List;
+import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.LiveValue;
+import com.google.gwt.aria.client.Roles;
+import com.google.gwt.aria.client.SelectedValue;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Style;
@@ -55,6 +59,7 @@ import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.VCaption;
+import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.ComponentStateUtil;
@@ -94,12 +99,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
+ "-selected";
private static final String TD_SELECTED_FIRST_CLASSNAME = TD_SELECTED_CLASSNAME
+ "-first";
+ private static final String TD_FOCUS_CLASSNAME = TD_CLASSNAME
+ + "-focus";
+ private static final String TD_FOCUS_FIRST_CLASSNAME = TD_FOCUS_CLASSNAME
+ + "-first";
private static final String TD_DISABLED_CLASSNAME = TD_CLASSNAME
+ "-disabled";
private static final String DIV_CLASSNAME = CLASSNAME + "-tabitem";
private static final String DIV_SELECTED_CLASSNAME = DIV_CLASSNAME
+ "-selected";
+ private static final String DIV_FOCUS_CLASSNAME = DIV_CLASSNAME
+ + "-focus";
private TabCaption tabCaption;
Element td = getElement();
@@ -112,11 +123,17 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
private String styleName;
+ private String id;
+
private Tab(TabBar tabBar) {
super(DOM.createTD());
this.tabBar = tabBar;
setStyleName(td, TD_CLASSNAME);
+ Roles.getTabRole().set(getElement());
+ Roles.getTabRole().setAriaSelectedState(getElement(),
+ SelectedValue.FALSE);
+
div = DOM.createDiv();
focusImpl.setTabIndex(td, -1);
setStyleName(div, DIV_CLASSNAME);
@@ -127,6 +144,9 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
.getApplicationConnection());
add(tabCaption);
+ Roles.getTabRole().setAriaLabelledbyProperty(getElement(),
+ Id.of(tabCaption.getElement()));
+
addFocusHandler(getTabsheet());
addBlurHandler(getTabsheet());
addKeyDownHandler(getTabsheet());
@@ -138,6 +158,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void setHiddenOnServer(boolean hiddenOnServer) {
this.hiddenOnServer = hiddenOnServer;
+ Roles.getTabRole().setAriaHiddenState(getElement(), hiddenOnServer);
}
@Override
@@ -152,6 +173,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void setEnabledOnServer(boolean enabled) {
enabledOnServer = enabled;
+ Roles.getTabRole().setAriaDisabledState(getElement(), !enabled);
+
setStyleName(td, TD_DISABLED_CLASSNAME, !enabled);
if (!enabled) {
focusImpl.setTabIndex(td, -1);
@@ -175,10 +198,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
* true if the Tab is the first visible Tab
*/
public void setStyleNames(boolean selected, boolean first) {
+ setStyleNames(selected, first, false);
+ }
+
+ public void setStyleNames(boolean selected, boolean first,
+ boolean keyboardFocus) {
setStyleName(td, TD_FIRST_CLASSNAME, first);
setStyleName(td, TD_SELECTED_CLASSNAME, selected);
setStyleName(td, TD_SELECTED_FIRST_CLASSNAME, selected && first);
setStyleName(div, DIV_SELECTED_CLASSNAME, selected);
+ setStyleName(td, TD_FOCUS_CLASSNAME, keyboardFocus);
+ setStyleName(td, TD_FOCUS_FIRST_CLASSNAME, keyboardFocus && first);
+ setStyleName(div, DIV_FOCUS_CLASSNAME, keyboardFocus);
}
public void setTabulatorIndex(int tabIndex) {
@@ -204,10 +235,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
String newStyleName = tabUidl
.getStringAttribute(TabsheetConstants.TAB_STYLE_NAME);
// Find the nth td element
- if (newStyleName != null && newStyleName.length() != 0) {
+ if (newStyleName != null && !newStyleName.isEmpty()) {
if (!newStyleName.equals(styleName)) {
// If we have a new style name
- if (styleName != null && styleName.length() != 0) {
+ if (styleName != null && !styleName.isEmpty()) {
// Remove old style name if present
td.removeClassName(TD_CLASSNAME + "-" + styleName);
}
@@ -221,6 +252,15 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
td.removeClassName(TD_CLASSNAME + "-" + styleName);
styleName = null;
}
+
+ String newId = tabUidl.getStringAttribute("id");
+ if (newId != null && !newId.isEmpty()) {
+ td.setId(newId);
+ id = newId;
+ } else if (id != null) {
+ td.removeAttribute("id");
+ id = null;
+ }
}
public void recalculateCaptionWidth() {
@@ -249,6 +289,23 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void blur() {
focusImpl.blur(td);
}
+
+ public boolean hasTooltip() {
+ return tabCaption.getTooltipInfo() != null;
+ }
+
+ public TooltipInfo getTooltipInfo() {
+ return tabCaption.getTooltipInfo();
+ }
+
+ public void setAssistiveDescription(String descriptionId) {
+ Roles.getTablistRole().setAriaDescribedbyProperty(getElement(),
+ Id.of(descriptionId));
+ }
+
+ public void removeAssistiveDescription() {
+ Roles.getTablistRole().removeAriaDescribedbyProperty(getElement());
+ }
}
public static class TabCaption extends VCaption {
@@ -262,6 +319,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
super(client);
this.client = client;
this.tab = tab;
+
+ AriaHelper.ensureHasId(getElement());
}
public boolean updateCaption(UIDL uidl) {
@@ -280,7 +339,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DISABLED),
uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_DESCRIPTION),
uidl.hasAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ERROR_MESSAGE),
- uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON));
+ uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON),
+ uidl.getStringAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON_ALT));
setClosable(uidl.hasAttribute("closable"));
@@ -318,6 +378,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
closeButton.setInnerHTML("&times;");
closeButton
.setClassName(VTabsheet.CLASSNAME + "-caption-close");
+
+ Roles.getTabRole().setAriaHiddenState(closeButton, true);
+ Roles.getTabRole().setAriaDisabledState(closeButton, true);
+
getElement().appendChild(closeButton);
} else if (!closable && closeButton != null) {
getElement().removeChild(closeButton);
@@ -364,6 +428,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
this.tabsheet = tabsheet;
Element el = DOM.createTable();
+ Roles.getPresentationRole().set(el);
+
Element tbody = DOM.createTBody();
DOM.appendChild(el, tbody);
DOM.appendChild(tbody, tr);
@@ -420,11 +486,9 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
int index = getWidgetIndex(caption.getParent());
- // IE needs explicit focus()
- if (BrowserInfo.get().isIE()) {
- getTabsheet().focus();
- }
- getTabsheet().onTabSelected(index);
+
+ getTabsheet().focus();
+ getTabsheet().loadTabSheet(index);
}
public VTabsheet getTabsheet() {
@@ -442,13 +506,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
final Tab newSelected = getTab(index);
final Tab oldSelected = selected;
- newSelected.setStyleNames(true, isFirstVisibleTab(index));
+ newSelected.setStyleNames(true, isFirstVisibleTab(index), true);
newSelected.setTabulatorIndex(getTabsheet().tabulatorIndex);
+ Roles.getTabRole().setAriaSelectedState(newSelected.getElement(),
+ SelectedValue.TRUE);
if (oldSelected != null && oldSelected != newSelected) {
oldSelected.setStyleNames(false,
isFirstVisibleTab(getWidgetIndex(oldSelected)));
oldSelected.setTabulatorIndex(-1);
+
+ Roles.getTabRole().setAriaSelectedState(
+ oldSelected.getElement(), SelectedValue.FALSE);
}
// Update the field holding the currently selected tab
@@ -459,6 +528,23 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
getTab(tabsheet.activeTabIndex).recalculateCaptionWidth();
}
+ public void navigateTab(int fromIndex, int toIndex) {
+ Tab newNavigated = getTab(toIndex);
+ if (newNavigated == null) {
+ throw new IllegalArgumentException(
+ "Tab at provided index toIndex was not found");
+ }
+
+ Tab oldNavigated = getTab(fromIndex);
+ newNavigated.setStyleNames(newNavigated.equals(selected),
+ isFirstVisibleTab(toIndex), true);
+
+ if (oldNavigated != null && fromIndex != toIndex) {
+ oldNavigated.setStyleNames(oldNavigated.equals(selected),
+ isFirstVisibleTab(fromIndex), false);
+ }
+ }
+
public void removeTab(int i) {
Tab tab = getTab(i);
if (tab == null) {
@@ -591,7 +677,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* @return Whether the tab could be selected or not.
*/
- private boolean onTabSelected(final int tabIndex) {
+ private boolean canSelectTab(final int tabIndex) {
Tab tab = tb.getTab(tabIndex);
if (client == null || disabled || waitingForResponse) {
return false;
@@ -599,15 +685,32 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
if (!tab.isEnabledOnServer() || tab.isHiddenOnServer()) {
return false;
}
- if (activeTabIndex != tabIndex) {
+
+ // Note that we return true when tabIndex == activeTabIndex; the active
+ // tab could be selected, it's just a no-op.
+ return true;
+ }
+
+ /**
+ * Load the content of a tab of the provided index.
+ *
+ * @param index
+ * of the tab to load
+ */
+ public void loadTabSheet(int tabIndex) {
+ if (activeTabIndex != tabIndex && canSelectTab(tabIndex)) {
tb.selectTab(tabIndex);
// If this TabSheet already has focus, set the new selected tab
// as focused.
if (focusedTab != null) {
- focusedTab = tab;
+ focusedTab = tb.getTab(tabIndex);
+ focusedTab.focus();
}
+ activeTabIndex = tabIndex;
+ focusedTabIndex = tabIndex;
+
addStyleDependentName("loading");
// Hide the current contents so a loading indicator can be shown
// instead
@@ -619,9 +722,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
.toString(), true);
waitingForResponse = true;
}
- // Note that we return true when tabIndex == activeTabIndex; the active
- // tab could be selected, it's just a no-op.
- return true;
}
public ApplicationConnection getApplicationConnection() {
@@ -663,22 +763,30 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
DOM.setStyleAttribute(getElement(), "overflow", "hidden");
tabs = DOM.createDiv();
DOM.setElementProperty(tabs, "className", TABS_CLASSNAME);
+ Roles.getTablistRole().set(tabs);
+ Roles.getTablistRole().setAriaLiveProperty(tabs, LiveValue.OFF);
scroller = DOM.createDiv();
+ Roles.getTablistRole().setAriaHiddenState(scroller, true);
DOM.setElementProperty(scroller, "className", SCROLLER_CLASSNAME);
scrollerPrev = DOM.createButton();
+ scrollerPrev.setTabIndex(-1);
DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME
+ "Prev");
+ Roles.getTablistRole().setAriaHiddenState(scrollerPrev, true);
DOM.sinkEvents(scrollerPrev, Event.ONCLICK);
scrollerNext = DOM.createButton();
+ scrollerNext.setTabIndex(-1);
DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME
+ "Next");
+ Roles.getTablistRole().setAriaHiddenState(scrollerNext, true);
DOM.sinkEvents(scrollerNext, Event.ONCLICK);
DOM.appendChild(getElement(), tabs);
// Tabs
tp.setStyleName(CLASSNAME + "-tabsheetpanel");
contentNode = DOM.createDiv();
+ Roles.getTabpanelRole().set(contentNode);
deco = DOM.createDiv();
@@ -1083,7 +1191,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
@Override
public void onBlur(BlurEvent event) {
+ getApplicationConnection().getVTooltip().hideTooltip();
+
if (focusedTab != null && event.getSource() instanceof Tab) {
+ focusedTab.removeAssistiveDescription();
focusedTab = null;
if (client.hasEventListeners(this, EventId.BLUR)) {
client.updateVariable(id, EventId.BLUR, "", true);
@@ -1098,6 +1209,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
if (client.hasEventListeners(this, EventId.FOCUS)) {
client.updateVariable(id, EventId.FOCUS, "", true);
}
+
+ if (focusedTab.hasTooltip()) {
+ focusedTab.setAssistiveDescription(getApplicationConnection()
+ .getVTooltip().getUniqueId());
+ getApplicationConnection().getVTooltip().showAssistive(
+ focusedTab.getTooltipInfo());
+ }
}
}
@@ -1118,13 +1236,17 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
if (!event.isAnyModifierKeyDown()) {
if (keycode == getPreviousTabKey()) {
selectPreviousTab();
+ event.stopPropagation();
} else if (keycode == getNextTabKey()) {
selectNextTab();
+ event.stopPropagation();
} else if (keycode == getCloseTabKey()) {
Tab tab = tb.getTab(activeTabIndex);
if (tab.isClosable()) {
tab.onClose();
}
+ } else if (keycode == getSelectTabKey()) {
+ loadTabSheet(focusedTabIndex);
}
}
}
@@ -1138,6 +1260,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return KeyCodes.KEY_LEFT;
}
+ protected int getSelectTabKey() {
+ return 32; // Space key
+ }
+
/**
* @return The key code of the keyboard shortcut that selects the next tab
* in a focused tabsheet.
@@ -1155,18 +1281,27 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
private void selectPreviousTab() {
- int newTabIndex = activeTabIndex;
+ int newTabIndex = focusedTabIndex;
// Find the previous visible and enabled tab if any.
do {
newTabIndex--;
- } while (newTabIndex >= 0 && !onTabSelected(newTabIndex));
+ } while (newTabIndex >= 0 && !canSelectTab(newTabIndex));
if (newTabIndex >= 0) {
- activeTabIndex = newTabIndex;
+ tb.navigateTab(focusedTabIndex, newTabIndex);
+ focusedTabIndex = newTabIndex;
+
+ // If this TabSheet already has focus, set the new selected tab
+ // as focused.
+ if (focusedTab != null) {
+ focusedTab = tb.getTab(focusedTabIndex);
+ focusedTab.focus();
+ }
+
if (isScrolledTabs()) {
// Scroll until the new active tab is visible
int newScrollerIndex = scrollerIndex;
- while (tb.getTab(activeTabIndex).getAbsoluteLeft() < getAbsoluteLeft()
+ while (tb.getTab(focusedTabIndex).getAbsoluteLeft() < getAbsoluteLeft()
&& newScrollerIndex != -1) {
newScrollerIndex = tb.scrollLeft(newScrollerIndex);
}
@@ -1177,18 +1312,28 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
private void selectNextTab() {
- int newTabIndex = activeTabIndex;
+ int newTabIndex = focusedTabIndex;
// Find the next visible and enabled tab if any.
do {
newTabIndex++;
- } while (newTabIndex < getTabCount() && !onTabSelected(newTabIndex));
+ } while (newTabIndex < getTabCount() && !canSelectTab(newTabIndex));
if (newTabIndex < getTabCount()) {
- activeTabIndex = newTabIndex;
+
+ tb.navigateTab(focusedTabIndex, newTabIndex);
+ focusedTabIndex = newTabIndex;
+
+ // If this TabSheet already has focus, set the new selected tab
+ // as focused.
+ if (focusedTab != null) {
+ focusedTab = tb.getTab(focusedTabIndex);
+ focusedTab.focus();
+ }
+
if (isClippedTabs()) {
// Scroll until the new active tab is completely visible
int newScrollerIndex = scrollerIndex;
- while (isClipped(tb.getTab(activeTabIndex))
+ while (isClipped(tb.getTab(focusedTabIndex))
&& newScrollerIndex != -1) {
newScrollerIndex = tb.scrollRight(newScrollerIndex);
}
diff --git a/client/src/com/vaadin/client/ui/VTabsheetBase.java b/client/src/com/vaadin/client/ui/VTabsheetBase.java
index 0923248115..bcd8811c7d 100644
--- a/client/src/com/vaadin/client/ui/VTabsheetBase.java
+++ b/client/src/com/vaadin/client/ui/VTabsheetBase.java
@@ -42,6 +42,8 @@ public abstract class VTabsheetBase extends ComplexPanel {
/** For internal use only. May be removed or replaced in the future. */
public int activeTabIndex = 0;
/** For internal use only. May be removed or replaced in the future. */
+ public int focusedTabIndex = 0;
+ /** For internal use only. May be removed or replaced in the future. */
public boolean disabled;
/** For internal use only. May be removed or replaced in the future. */
public boolean readonly;
diff --git a/client/src/com/vaadin/client/ui/VTree.java b/client/src/com/vaadin/client/ui/VTree.java
index 51c00ca310..1acd4bd05f 100644
--- a/client/src/com/vaadin/client/ui/VTree.java
+++ b/client/src/com/vaadin/client/ui/VTree.java
@@ -70,6 +70,7 @@ import com.vaadin.client.ui.dd.VDragEvent;
import com.vaadin.client.ui.dd.VDropHandler;
import com.vaadin.client.ui.dd.VHasDropHandler;
import com.vaadin.client.ui.dd.VTransferable;
+import com.vaadin.client.ui.tree.TreeConnector;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.MouseEventDetails.MouseButton;
import com.vaadin.shared.ui.MultiSelectMode;
@@ -162,6 +163,9 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
/** For internal use only. May be removed or replaced in the future. */
public String[] bodyActionKeys;
+ /** For internal use only. May be removed or replaced in the future. */
+ public TreeConnector connector;
+
public VLazyExecutor iconLoaded = new VLazyExecutor(50,
new ScheduledCommand() {
@@ -1729,6 +1733,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
}
}
}
+ showTooltipForKeyboardNavigation(node);
return true;
}
@@ -1754,6 +1759,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
}
}
}
+ showTooltipForKeyboardNavigation(node);
return true;
}
@@ -1774,6 +1780,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
focusAndSelectNode(focusedNode.getParentNode());
}
}
+ showTooltipForKeyboardNavigation(focusedNode);
return true;
}
@@ -1792,6 +1799,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
focusAndSelectNode(focusedNode.getChildren().get(0));
}
}
+ showTooltipForKeyboardNavigation(focusedNode);
return true;
}
@@ -1820,6 +1828,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
selectNode(node, true);
}
sendSelectionToServer();
+ showTooltipForKeyboardNavigation(node);
return true;
}
@@ -1836,12 +1845,20 @@ public class VTree extends FocusElementPanel implements VHasDropHandler,
selectNode(node, true);
}
sendSelectionToServer();
+ showTooltipForKeyboardNavigation(node);
return true;
}
return false;
}
+ private void showTooltipForKeyboardNavigation(TreeNode node) {
+ if (connector != null) {
+ getClient().getVTooltip().showAssistive(
+ connector.getTooltipInfo(node.nodeCaptionSpan));
+ }
+ }
+
private void focusAndSelectNode(TreeNode node) {
/*
* Keyboard navigation doesn't work reliably if the tree is in
diff --git a/client/src/com/vaadin/client/ui/VUI.java b/client/src/com/vaadin/client/ui/VUI.java
index 4817bf9304..1a84613a5d 100644
--- a/client/src/com/vaadin/client/ui/VUI.java
+++ b/client/src/com/vaadin/client/ui/VUI.java
@@ -18,6 +18,7 @@ package com.vaadin.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.Element;
import com.google.gwt.event.dom.client.HasScrollHandlers;
@@ -43,6 +44,7 @@ import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.Profiler;
+import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;
@@ -165,6 +167,8 @@ public class VUI extends SimplePanel implements ResizeHandler,
});
+ private Element storedFocus;
+
public VUI() {
super();
// Allow focusing the view by using the focus() method, the view
@@ -497,4 +501,36 @@ public class VUI extends SimplePanel implements ResizeHandler,
FocusUtil.setTabIndex(this, index);
}
+ /**
+ * Allows to store the currently focused Element.
+ *
+ * Current use case is to store the focus when a Window is opened. Does
+ * currently handle only a single value. Needs to be extended for #12158
+ *
+ * @param focusedElement
+ */
+ public void storeFocus() {
+ storedFocus = Util.getFocusedElement();
+ }
+
+ /**
+ * Restores the previously stored focus Element.
+ *
+ * Current use case is to restore the focus when a Window is closed. Does
+ * currently handle only a single value. Needs to be extended for #12158
+ *
+ * @return the lastFocusElementBeforeDialogOpened
+ */
+ public void focusStoredElement() {
+ if (storedFocus != null) {
+ storedFocus.focus();
+
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ storedFocus.focus();
+ }
+ });
+ }
+ }
}
diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java
index ff6a15e597..03ad8d03c8 100644
--- a/client/src/com/vaadin/client/ui/VWindow.java
+++ b/client/src/com/vaadin/client/ui/VWindow.java
@@ -18,10 +18,16 @@ package com.vaadin.client.ui;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
+import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.RelevantValue;
+import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
@@ -29,34 +35,45 @@ import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
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.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorMap;
import com.vaadin.client.Focusable;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.Util;
import com.vaadin.client.debug.internal.VDebugWindow;
import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
+import com.vaadin.client.ui.aria.AriaHelper;
+import com.vaadin.shared.Connector;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.window.WindowMode;
+import com.vaadin.shared.ui.window.WindowState.WindowRole;
/**
* "Sub window" component.
*
* @author Vaadin Ltd
*/
-public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
- ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable {
+public class VWindow extends VWindowOverlay implements
+ ShortcutActionHandlerOwner, ScrollHandler, KeyDownHandler,
+ KeyUpHandler, FocusHandler, BlurHandler, Focusable {
private static ArrayList<VWindow> windowOrder = new ArrayList<VWindow>();
@@ -138,6 +155,22 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
private boolean closable = true;
+ private Connector[] assistiveConnectors = new Connector[0];
+ private String assistivePrefix;
+ private String assistivePostfix;
+
+ private Element topTabStop;
+ private Element bottomTabStop;
+
+ private NativePreviewHandler topEventBlocker;
+ private NativePreviewHandler bottomEventBlocker;
+
+ private HandlerRegistration topBlockerRegistration;
+ private HandlerRegistration bottomBlockerRegistration;
+
+ // Prevents leaving the window with the Tab key when true
+ private boolean doTabStop;
+
/**
* If centered (via UIDL), the window should stay in the centered -mode
* until a position is received from the server, or the user moves or
@@ -172,13 +205,74 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
// Different style of shadow for windows
setShadowStyle("window");
+ Roles.getDialogRole().set(getElement());
+ Roles.getDialogRole().setAriaRelevantProperty(getElement(),
+ RelevantValue.ADDITIONS);
+
constructDOM();
contentPanel.addScrollHandler(this);
contentPanel.addKeyDownHandler(this);
+ contentPanel.addKeyUpHandler(this);
contentPanel.addFocusHandler(this);
contentPanel.addBlurHandler(this);
}
+ @Override
+ protected void onAttach() {
+ super.onAttach();
+
+ /*
+ * Stores the element that has focus in the application UI when the
+ * window is opened, so it can be restored when the window closes.
+ *
+ * This is currently implemented for the case when one non-modal window
+ * can be open at the same time, and the focus is not changed while the
+ * window is open.
+ */
+ getApplicationConnection().getUIConnector().getWidget().storeFocus();
+
+ /*
+ * When this window gets reattached, set the tabstop to the previous
+ * state.
+ */
+ setTabStopEnabled(doTabStop);
+ }
+
+ @Override
+ protected void onDetach() {
+ super.onDetach();
+
+ /*
+ * Restores the previously stored focused element.
+ *
+ * When the focus was changed outside the window while the window was
+ * open, the originally stored element is restored.
+ */
+ getApplicationConnection().getUIConnector().getWidget()
+ .focusStoredElement();
+
+ removeTabBlockHandlers();
+ }
+
+ private void addTabBlockHandlers() {
+ if (topBlockerRegistration == null) {
+ topBlockerRegistration = Event
+ .addNativePreviewHandler(topEventBlocker);
+ bottomBlockerRegistration = Event
+ .addNativePreviewHandler(bottomEventBlocker);
+ }
+ }
+
+ private void removeTabBlockHandlers() {
+ if (topBlockerRegistration != null) {
+ topBlockerRegistration.removeHandler();
+ topBlockerRegistration = null;
+
+ bottomBlockerRegistration.removeHandler();
+ bottomBlockerRegistration = null;
+ }
+ }
+
public void bringToFront() {
int curIndex = windowOrder.indexOf(this);
if (curIndex + 1 < windowOrder.size()) {
@@ -242,6 +336,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
protected void constructDOM() {
setStyleName(CLASSNAME);
+ topTabStop = DOM.createDiv();
+ DOM.setElementAttribute(topTabStop, "tabindex", "0");
+
header = DOM.createDiv();
DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
headerText = DOM.createDiv();
@@ -256,18 +353,25 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
maximizeRestoreBox = DOM.createDiv();
DOM.setElementProperty(maximizeRestoreBox, "className", CLASSNAME
+ "-maximizebox");
+ DOM.setElementAttribute(maximizeRestoreBox, "tabindex", "0");
DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox");
+ DOM.setElementAttribute(closeBox, "tabindex", "0");
DOM.appendChild(footer, resizeBox);
+ bottomTabStop = DOM.createDiv();
+ DOM.setElementAttribute(bottomTabStop, "tabindex", "0");
+
wrapper = DOM.createDiv();
DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
+ DOM.appendChild(wrapper, topTabStop);
DOM.appendChild(wrapper, header);
DOM.appendChild(wrapper, maximizeRestoreBox);
DOM.appendChild(wrapper, closeBox);
DOM.appendChild(header, headerText);
DOM.appendChild(wrapper, contents);
DOM.appendChild(wrapper, footer);
+ DOM.appendChild(wrapper, bottomTabStop);
DOM.appendChild(super.getContainerElement(), wrapper);
sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS
@@ -275,6 +379,96 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
setWidget(contentPanel);
+ // Make the closebox accessible for assistive devices
+ Roles.getButtonRole().set(closeBox);
+ Roles.getButtonRole().setAriaLabelProperty(closeBox, "close button");
+
+ // Make the maximizebox accessible for assistive devices
+ Roles.getButtonRole().set(maximizeRestoreBox);
+ Roles.getButtonRole().setAriaLabelProperty(maximizeRestoreBox,
+ "maximize button");
+
+ // Provide the title to assistive devices
+ AriaHelper.ensureHasId(headerText);
+ Roles.getDialogRole().setAriaLabelledbyProperty(getElement(),
+ Id.of(headerText));
+
+ // Handlers to Prevent tab to leave the window
+ topEventBlocker = new NativePreviewHandler() {
+ @Override
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ NativeEvent nativeEvent = event.getNativeEvent();
+ if (nativeEvent.getEventTarget().cast() == topTabStop
+ && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
+ && nativeEvent.getShiftKey()) {
+ nativeEvent.preventDefault();
+ }
+ }
+ };
+
+ bottomEventBlocker = new NativePreviewHandler() {
+ @Override
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ NativeEvent nativeEvent = event.getNativeEvent();
+ if (nativeEvent.getEventTarget().cast() == bottomTabStop
+ && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
+ && !nativeEvent.getShiftKey()) {
+ nativeEvent.preventDefault();
+ }
+ }
+ };
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param topMessage
+ * String provided when the user navigates with Shift-Tab keys to
+ * the top of the window
+ */
+ public void setTabStopTopAssistiveText(String topMessage) {
+ Roles.getNoteRole().setAriaLabelProperty(topTabStop, topMessage);
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param bottomMessage
+ * String provided when the user navigates with the Tab key to
+ * the bottom of the window
+ */
+ public void setTabStopBottomAssistiveText(String bottomMessage) {
+ Roles.getNoteRole().setAriaLabelProperty(bottomTabStop, bottomMessage);
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ *
+ * @return the top message
+ */
+ public String getTabStopTopAssistiveText() {
+ return Roles.getNoteRole().getAriaLabelProperty(topTabStop);
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ *
+ * @return the bottom message
+ */
+ public String getTabStopBottomAssistiveText() {
+ return Roles.getNoteRole().getAriaLabelProperty(bottomTabStop);
}
/**
@@ -492,6 +686,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
if (isAttached()) {
showModalityCurtain();
}
+ addTabBlockHandlers();
deferOrdering();
} else {
if (modalityCurtain != null) {
@@ -500,6 +695,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
modalityCurtain = null;
}
+ if (!doTabStop) {
+ removeTabBlockHandlers();
+ }
}
}
@@ -653,11 +851,64 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
if (icon != null) {
icon = client.translateVaadinUri(icon);
html = "<img src=\"" + Util.escapeAttribute(icon)
- + "\" class=\"v-icon\" />" + html;
+ + "\" class=\"v-icon\" alt=\"\" />" + html;
}
+
+ // Provide information to assistive device users that a sub window was
+ // opened
+ String prefix = "<span class='"
+ + AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE + "'>"
+ + assistivePrefix + "</span>";
+ String postfix = "<span class='"
+ + AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE + "'>"
+ + assistivePostfix + "</span>";
+
+ html = prefix + html + postfix;
DOM.setInnerHTML(headerText, html);
}
+ /**
+ * Setter for the text for assistive devices the window caption is prefixed
+ * with.
+ *
+ * @param assistivePrefix
+ * the assistivePrefix to set
+ */
+ public void setAssistivePrefix(String assistivePrefix) {
+ this.assistivePrefix = assistivePrefix;
+ }
+
+ /**
+ * Getter for the text for assistive devices the window caption is prefixed
+ * with.
+ *
+ * @return the assistivePrefix
+ */
+ public String getAssistivePrefix() {
+ return assistivePrefix;
+ }
+
+ /**
+ * Setter for the text for assistive devices the window caption is postfixed
+ * with.
+ *
+ * @param assistivePostfix
+ * the assistivePostfix to set
+ */
+ public void setAssistivePostfix(String assistivePostfix) {
+ this.assistivePostfix = assistivePostfix;
+ }
+
+ /**
+ * Getter for the text for assistive devices the window caption is postfixed
+ * with.
+ *
+ * @return the assistivePostfix
+ */
+ public String getAssistivePostfix() {
+ return assistivePostfix;
+ }
+
@Override
protected Element getContainerElement() {
// in GWT 1.5 this method is used in PopupPanel constructor
@@ -1026,6 +1277,13 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
}
@Override
+ public void onKeyUp(KeyUpEvent event) {
+ if (isClosable() && event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
+ onCloseClick();
+ }
+ }
+
+ @Override
public void onBlur(BlurEvent event) {
if (client.hasEventListeners(this, EventId.BLUR)) {
client.updateVariable(id, EventId.BLUR, "", true);
@@ -1061,4 +1319,97 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
- contentPanel.getElement().getOffsetWidth();
}
+ /**
+ * Allows to specify which connectors contain the description for the
+ * window. Text contained in the widgets of the connectors will be read by
+ * assistive devices when it is opened.
+ * <p>
+ * When the provided array is empty, an existing description is removed.
+ *
+ * @param connectors
+ * with the connectors of the widgets to use as description
+ */
+ public void setAssistiveDescription(Connector[] connectors) {
+ if (connectors != null) {
+ assistiveConnectors = connectors;
+
+ if (connectors.length == 0) {
+ Roles.getDialogRole().removeAriaDescribedbyProperty(
+ getElement());
+ } else {
+ Id[] ids = new Id[connectors.length];
+ for (int index = 0; index < connectors.length; index++) {
+ if (connectors[index] == null) {
+ throw new IllegalArgumentException(
+ "All values in parameter description need to be non-null");
+ }
+
+ Element element = ((ComponentConnector) connectors[index])
+ .getWidget().getElement();
+ AriaHelper.ensureHasId(element);
+ ids[index] = Id.of(element);
+ }
+
+ Roles.getDialogRole().setAriaDescribedbyProperty(getElement(),
+ ids);
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "Parameter description must be non-null");
+ }
+ }
+
+ /**
+ * Gets the connectors that are used as assistive description. Text
+ * contained in these connectors will be read by assistive devices when the
+ * window is opened.
+ *
+ * @return list of previously set connectors
+ */
+ public List<Connector> getAssistiveDescription() {
+ return Collections.unmodifiableList(Arrays.asList(assistiveConnectors));
+ }
+
+ /**
+ * Sets the WAI-ARIA role the window.
+ *
+ * This role defines how an assistive device handles a window. Available
+ * roles are alertdialog and dialog (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * The default role is dialog.
+ *
+ * @param role
+ * WAI-ARIA role to set for the window
+ */
+ public void setWaiAriaRole(WindowRole role) {
+ if ("alertdialog".equals(role)) {
+ Roles.getAlertdialogRole().set(getElement());
+ } else {
+ Roles.getDialogRole().set(getElement());
+ }
+ }
+
+ /**
+ * Registers the handlers that prevent to leave the window using the
+ * Tab-key.
+ * <p>
+ * The value of the parameter doTabStop is stored and used for non-modal
+ * windows. For modal windows, the handlers are always registered, while
+ * preserving the stored value.
+ *
+ * @param doTabStop
+ * true to prevent leaving the window, false to allow leaving the
+ * window for non modal windows
+ */
+ public void setTabStopEnabled(boolean doTabStop) {
+ this.doTabStop = doTabStop;
+
+ if (doTabStop || vaadinModality) {
+ addTabBlockHandlers();
+ } else {
+ removeTabBlockHandlers();
+ }
+ }
}
diff --git a/client/src/com/vaadin/client/ui/VWindowOverlay.java b/client/src/com/vaadin/client/ui/VWindowOverlay.java
new file mode 100644
index 0000000000..efc01cf63e
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/VWindowOverlay.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.ui;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.vaadin.client.ApplicationConnection;
+
+public class VWindowOverlay extends VOverlay {
+ public VWindowOverlay() {
+ }
+
+ public VWindowOverlay(boolean autoHide, boolean modal, boolean showShadow) {
+ super(autoHide, modal, showShadow);
+ }
+
+ /**
+ * Gets the 'overlay container' element. Tries to find the current
+ * {@link ApplicationConnection} using {@link #getApplicationConnection()}.
+ *
+ * @return the overlay container element for the current
+ * {@link ApplicationConnection} or another element if the current
+ * {@link ApplicationConnection} cannot be determined.
+ */
+ @Override
+ public Element getOverlayContainer() {
+ ApplicationConnection ac = getApplicationConnection();
+ if (ac == null) {
+ return super.getOverlayContainer();
+ } else {
+ Element overlayContainer = getOverlayContainer(ac);
+ return overlayContainer;
+ }
+ }
+
+ /**
+ * Gets the 'overlay container' element pertaining to the given
+ * {@link ApplicationConnection}. Each overlay should be created in a
+ * overlay container element, so that the correct theme and styles can be
+ * applied.
+ *
+ * @param ac
+ * A reference to {@link ApplicationConnection}
+ * @return The overlay container
+ */
+ public static Element getOverlayContainer(ApplicationConnection ac) {
+ String id = ac.getConfiguration().getRootPanelId();
+ id = id += "-window-overlays";
+ Element container = DOM.getElementById(id);
+ if (container == null) {
+ container = DOM.createDiv();
+ container.setId(id);
+ String styles = ac.getUIConnector().getWidget().getParent()
+ .getStyleName();
+ container.addClassName(styles);
+ container.addClassName(CLASSNAME_CONTAINER);
+ RootPanel.get().getElement().appendChild(container);
+ }
+
+ return container;
+ }
+}
diff --git a/client/src/com/vaadin/client/ui/button/ButtonConnector.java b/client/src/com/vaadin/client/ui/button/ButtonConnector.java
index a6630f28b9..9a63808742 100644
--- a/client/src/com/vaadin/client/ui/button/ButtonConnector.java
+++ b/client/src/com/vaadin/client/ui/button/ButtonConnector.java
@@ -87,8 +87,7 @@ public class ButtonConnector extends AbstractComponentConnector implements
getWidget().icon.getElement(),
getWidget().captionElement);
}
- getWidget().icon.setUri(getIcon());
- getWidget().icon.setAlternateText(getState().iconAltText);
+ getWidget().icon.setUri(getIcon(), getState().iconAltText);
} else {
if (getWidget().icon != null) {
getWidget().wrapper.removeChild(getWidget().icon
diff --git a/client/src/com/vaadin/client/ui/dd/VHtml5File.java b/client/src/com/vaadin/client/ui/dd/VHtml5File.java
index 4b36e7fd1b..c4ad615fbd 100644
--- a/client/src/com/vaadin/client/ui/dd/VHtml5File.java
+++ b/client/src/com/vaadin/client/ui/dd/VHtml5File.java
@@ -35,7 +35,13 @@ public class VHtml5File extends JavaScriptObject {
return this.type;
}-*/;
- public native final int getSize()
+ /*
+ * Browser implementations support files >2GB dropped and report the value
+ * as long. Due to JSNI limitations this value needs to be sent as double
+ * and then cast back to a long value.
+ * www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html#important
+ */
+ public native final double getSize()
/*-{
return this.size ? this.size : 0;
}-*/;
diff --git a/client/src/com/vaadin/client/ui/link/LinkConnector.java b/client/src/com/vaadin/client/ui/link/LinkConnector.java
index 228897278e..d2c41e9f38 100644
--- a/client/src/com/vaadin/client/ui/link/LinkConnector.java
+++ b/client/src/com/vaadin/client/ui/link/LinkConnector.java
@@ -17,38 +17,21 @@
package com.vaadin.client.ui.link;
import com.google.gwt.user.client.DOM;
-import com.vaadin.client.ApplicationConnection;
-import com.vaadin.client.Paintable;
-import com.vaadin.client.UIDL;
import com.vaadin.client.communication.StateChangeEvent;
-import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.VLink;
-import com.vaadin.shared.ui.BorderStyle;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.link.LinkConstants;
import com.vaadin.shared.ui.link.LinkState;
import com.vaadin.ui.Link;
@Connect(Link.class)
-public class LinkConnector extends AbstractComponentConnector implements
- Paintable {
+public class LinkConnector extends AbstractComponentConnector {
@Override
protected void init() {
super.init();
- addStateChangeHandler("resources", new StateChangeHandler() {
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- getWidget().src = getResourceUrl(LinkConstants.HREF_RESOURCE);
- if (getWidget().src == null) {
- getWidget().anchor.removeAttribute("href");
- } else {
- getWidget().anchor.setAttribute("href", getWidget().src);
- }
- }
- });
}
@Override
@@ -62,35 +45,30 @@ public class LinkConnector extends AbstractComponentConnector implements
}
@Override
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
-
- if (!isRealUpdate(uidl)) {
- return;
- }
-
- getWidget().client = client;
+ public void onStateChanged(StateChangeEvent stateChangeEvent) {
+ super.onStateChanged(stateChangeEvent);
getWidget().enabled = isEnabled();
- if (uidl.hasAttribute("name")) {
- getWidget().target = uidl.getStringAttribute("name");
- getWidget().anchor.setAttribute("target", getWidget().target);
- }
-
- if (uidl.hasAttribute("border")) {
- if ("none".equals(uidl.getStringAttribute("border"))) {
- getWidget().borderStyle = BorderStyle.NONE;
+ if (stateChangeEvent.hasPropertyChanged("resources")) {
+ getWidget().src = getResourceUrl(LinkConstants.HREF_RESOURCE);
+ if (getWidget().src == null) {
+ getWidget().anchor.removeAttribute("href");
} else {
- getWidget().borderStyle = BorderStyle.MINIMAL;
+ getWidget().anchor.setAttribute("href", getWidget().src);
}
+ }
+
+ getWidget().target = getState().target;
+ if (getWidget().target == null) {
+ getWidget().anchor.removeAttribute("target");
} else {
- getWidget().borderStyle = BorderStyle.DEFAULT;
+ getWidget().anchor.setAttribute("target", getWidget().target);
}
- getWidget().targetHeight = uidl.hasAttribute("targetHeight") ? uidl
- .getIntAttribute("targetHeight") : -1;
- getWidget().targetWidth = uidl.hasAttribute("targetWidth") ? uidl
- .getIntAttribute("targetWidth") : -1;
+ getWidget().borderStyle = getState().targetBorder;
+ getWidget().targetWidth = getState().targetWidth;
+ getWidget().targetHeight = getState().targetHeight;
// Set link caption
getWidget().captionElement.setInnerText(getState().caption);
@@ -111,13 +89,12 @@ public class LinkConnector extends AbstractComponentConnector implements
if (getIcon() != null) {
if (getWidget().icon == null) {
- getWidget().icon = new Icon(client);
+ getWidget().icon = new Icon(getConnection());
getWidget().anchor.insertBefore(getWidget().icon.getElement(),
getWidget().captionElement);
}
getWidget().icon.setUri(getIcon());
}
-
}
@Override
diff --git a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java
index 11a76b483b..2253397b16 100644
--- a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java
+++ b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java
@@ -94,7 +94,7 @@ public class NativeButtonConnector extends AbstractComponentConnector implements
getWidget().icon.getElement(),
getWidget().captionElement);
}
- getWidget().icon.setUri(getIcon());
+ getWidget().icon.setUri(getIcon(), getState().iconAltText);
} else {
if (getWidget().icon != null) {
getWidget().getElement().removeChild(
diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java
index 47229dc9c7..f1710351bf 100644
--- a/client/src/com/vaadin/client/ui/table/TableConnector.java
+++ b/client/src/com/vaadin/client/ui/table/TableConnector.java
@@ -264,7 +264,7 @@ public class TableConnector extends AbstractHasComponentsConnector implements
if (getWidget().focusedRow != null) {
if (!getWidget().focusedRow.isAttached()
- && !getWidget().rowRequestHandler.isRunning()) {
+ && !getWidget().rowRequestHandler.isRequestHandlerRunning()) {
// focused row has been orphaned, can't focus
if (getWidget().selectedRowKeys.contains(getWidget().focusedRow
.getKey())) {
diff --git a/client/src/com/vaadin/client/ui/tree/TreeConnector.java b/client/src/com/vaadin/client/ui/tree/TreeConnector.java
index ef016c31b7..6f89137918 100644
--- a/client/src/com/vaadin/client/ui/tree/TreeConnector.java
+++ b/client/src/com/vaadin/client/ui/tree/TreeConnector.java
@@ -45,6 +45,11 @@ public class TreeConnector extends AbstractComponentConnector implements
protected final Map<TreeNode, TooltipInfo> tooltipMap = new HashMap<TreeNode, TooltipInfo>();
@Override
+ protected void init() {
+ getWidget().connector = this;
+ }
+
+ @Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
if (!isRealUpdate(uidl)) {
return;
diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java
index 46b5f63180..903a2cc3fb 100644
--- a/client/src/com/vaadin/client/ui/ui/UIConnector.java
+++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java
@@ -101,10 +101,6 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
protected void init() {
super.init();
registerRpc(PageClientRpc.class, new PageClientRpc() {
- @Override
- public void setTitle(String title) {
- com.google.gwt.user.client.Window.setTitle(title);
- }
@Override
public void reload() {
@@ -666,6 +662,11 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
configurePolling();
}
+ if (stateChangeEvent.hasPropertyChanged("pageState.title")) {
+ com.google.gwt.user.client.Window
+ .setTitle(getState().pageState.title);
+ }
+
if (stateChangeEvent.hasPropertyChanged("pushConfiguration")) {
getConnection().setPushEnabled(
getState().pushConfiguration.mode.isEnabled());
diff --git a/client/src/com/vaadin/client/ui/window/WindowConnector.java b/client/src/com/vaadin/client/ui/window/WindowConnector.java
index 4b839384a2..464ab386c1 100644
--- a/client/src/com/vaadin/client/ui/window/WindowConnector.java
+++ b/client/src/com/vaadin/client/ui/window/WindowConnector.java
@@ -295,8 +295,18 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
if (getIcon() != null) {
iconURL = getIcon();
}
+
+ window.setAssistivePrefix(state.assistivePrefix);
+ window.setAssistivePostfix(state.assistivePostfix);
window.setCaption(state.caption, iconURL);
+ window.setWaiAriaRole(getState().role);
+ window.setAssistiveDescription(state.contentDescription);
+
+ window.setTabStopEnabled(getState().assistiveTabStop);
+ window.setTabStopTopAssistiveText(getState().assistiveTabStopTopText);
+ window.setTabStopBottomAssistiveText(getState().assistiveTabStopBottomText);
+
clickEventHandler.handleEventHandlerRegistration();
window.immediate = state.immediate;
diff --git a/scripts/automerge7.sh b/scripts/automerge7.sh
index bc3a7be0a4..10e6ee0ffb 100755
--- a/scripts/automerge7.sh
+++ b/scripts/automerge7.sh
@@ -1,8 +1,10 @@
#!/bin/bash
-FROM=7.0
-TO=7.1
+IGNORE=7.0
+FROM=7.1
+TO=master
+IGNORE_HEAD=origin/$IGNORE
FROM_HEAD=origin/$FROM
PUSH="origin HEAD:refs/for/$TO"
@@ -86,7 +88,7 @@ fi
git checkout $TO
git fetch
-pending=`git log $TO..$FROM_HEAD --reverse|grep "^commit "|sed "s/commit //"`
+pending=`git log $TO..$FROM_HEAD ^$IGNORE_HEAD --reverse|grep "^commit "|sed "s/commit //"`
pendingCommit=
pendingCommitMessage=
@@ -94,7 +96,7 @@ for commit in $pending
do
echo "Checking $commit..."
mergeDirective=`git log -n 1 --format=%B $commit|grep "^Merge:"|sed "s/Merge: //"`
- commitMsg=`git log -n 1 --format=oneline --abbrev-commit $commit`
+ commitMsg=`git log -n 1 --format=oneline --abbrev-commit $commit | sed 's/\\\\/\\\\\\\\/g'` #Multiple levels of unescaping, sed just changes \ to \\
if [ "$mergeDirective" == "" ]
then
if can_merge $commit
@@ -107,7 +109,7 @@ do
pendingCommit=
pendingCommitMessage=
echo
- echo "Stopping merge because $commit because of merge conflicts"
+ echo "Stopping merge at $commit because of merge conflicts"
echo "The following commit must be manually merged."
show $commit
exit 3
diff --git a/server/src/com/vaadin/annotations/Widgetset.java b/server/src/com/vaadin/annotations/Widgetset.java
index 40276c18a2..006bf59acf 100644
--- a/server/src/com/vaadin/annotations/Widgetset.java
+++ b/server/src/com/vaadin/annotations/Widgetset.java
@@ -24,7 +24,7 @@ import java.lang.annotation.Target;
import com.vaadin.ui.UI;
/**
- * Defines a specific theme for a {@link UI}.
+ * Defines a specific widgetset for a {@link UI}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
diff --git a/server/src/com/vaadin/data/Container.java b/server/src/com/vaadin/data/Container.java
index e93db52a35..bf553f31d2 100644
--- a/server/src/com/vaadin/data/Container.java
+++ b/server/src/com/vaadin/data/Container.java
@@ -582,6 +582,60 @@ public interface Container extends Serializable {
public Item addItemAt(int index, Object newItemId)
throws UnsupportedOperationException;
+ /**
+ * An <code>Event</code> object specifying information about the added
+ * items.
+ */
+ public interface ItemAddEvent extends ItemSetChangeEvent {
+
+ /**
+ * Gets the item id of the first added item.
+ *
+ * @return item id of the first added item
+ */
+ public Object getFirstItemId();
+
+ /**
+ * Gets the index of the first added item.
+ *
+ * @return index of the first added item
+ */
+ public int getFirstIndex();
+
+ /**
+ * Gets the number of the added items.
+ *
+ * @return the number of added items.
+ */
+ public int getAddedItemsCount();
+ }
+
+ /**
+ * An <code>Event</code> object specifying information about the removed
+ * items.
+ */
+ public interface ItemRemoveEvent extends ItemSetChangeEvent {
+ /**
+ * Gets the item id of the first removed item.
+ *
+ * @return item id of the first removed item
+ */
+ public Object getFirstItemId();
+
+ /**
+ * Gets the index of the first removed item.
+ *
+ * @return index of the first removed item
+ */
+ public int getFirstIndex();
+
+ /**
+ * Gets the number of the removed items.
+ *
+ * @return the number of removed items
+ */
+ public int getRemovedItemsCount();
+ }
}
/**
diff --git a/server/src/com/vaadin/data/util/AbstractBeanContainer.java b/server/src/com/vaadin/data/util/AbstractBeanContainer.java
index 35403d6419..67239996a2 100644
--- a/server/src/com/vaadin/data/util/AbstractBeanContainer.java
+++ b/server/src/com/vaadin/data/util/AbstractBeanContainer.java
@@ -222,6 +222,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
@Override
public boolean removeAllItems() {
int origSize = size();
+ IDTYPE firstItem = getFirstVisibleItem();
internalRemoveAllItems();
@@ -234,7 +235,7 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
// fire event only if the visible view changed, regardless of whether
// filtered out items were removed or not
if (origSize != 0) {
- fireItemSetChange();
+ fireItemsRemoved(0, firstItem, origSize);
}
return true;
@@ -679,6 +680,8 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
protected void addAll(Collection<? extends BEANTYPE> collection)
throws IllegalStateException, IllegalArgumentException {
boolean modified = false;
+ int origSize = size();
+
for (BEANTYPE bean : collection) {
// TODO skipping invalid beans - should not allow them in javadoc?
if (bean == null
@@ -699,13 +702,22 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
if (modified) {
// Filter the contents when all items have been added
if (isFiltered()) {
- filterAll();
- } else {
- fireItemSetChange();
+ doFilterContainer(!getFilters().isEmpty());
+ }
+ if (visibleNewItemsWasAdded(origSize)) {
+ // fire event about added items
+ int firstPosition = origSize;
+ IDTYPE firstItemId = getVisibleItemIds().get(firstPosition);
+ int affectedItems = size() - origSize;
+ fireItemsAdded(firstPosition, firstItemId, affectedItems);
}
}
}
+ private boolean visibleNewItemsWasAdded(int origSize) {
+ return size() > origSize;
+ }
+
/**
* Use the bean resolver to get the identifier for a bean.
*
@@ -845,8 +857,32 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
* @return true if the property was added
*/
public boolean addNestedContainerProperty(String propertyId) {
+ return addNestedContainerProperty(propertyId, false);
+ }
+
+ /**
+ * Adds a nested container property for the container, e.g.
+ * "manager.address.street".
+ *
+ * All intermediate getters must exist and must return non-null values when
+ * the property value is accessed or the <code>nullBeansAllowed</code> must
+ * be set to true. If the <code>nullBeansAllowed</code> flag is set to true,
+ * calling getValue of the added property will return null if the property
+ * or any of its intermediate getters returns null. If set to false, null
+ * values returned by intermediate getters will cause NullPointerException.
+ * The default value is false to ensure backwards compatibility.
+ *
+ * @see NestedMethodProperty
+ *
+ * @param propertyId
+ * @param nullBeansAllowed
+ * set true to allow null values from intermediate getters
+ * @return true if the property was added
+ */
+ public boolean addNestedContainerProperty(String propertyId,
+ boolean nullBeansAllowed) {
return addContainerProperty(propertyId, new NestedPropertyDescriptor(
- propertyId, type));
+ propertyId, type, nullBeansAllowed));
}
/**
@@ -864,13 +900,42 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends
*/
@SuppressWarnings("unchecked")
public void addNestedContainerBean(String propertyId) {
+ addNestedContainerBean(propertyId, false);
+ }
+
+ /**
+ * Adds a nested container properties for all sub-properties of a named
+ * property to the container. The named property itself is removed from the
+ * model as its subproperties are added.
+ *
+ * Unless
+ * <code>nullBeansAllowed<code> is set to true, all intermediate getters must
+ * exist and must return non-null values when the property values are
+ * accessed. If the <code>nullBeansAllowed</code> flag is set to true,
+ * calling getValue of the added subproperties will return null if the
+ * property or any of their intermediate getters returns null. If set to
+ * false, null values returned by intermediate getters will cause
+ * NullPointerException. The default value is false to ensure backwards
+ * compatibility.
+ *
+ * @see NestedMethodProperty
+ * @see #addNestedContainerProperty(String)
+ *
+ * @param propertyId
+ * @param nullBeansAllowed
+ * set true to allow null values from intermediate getters
+ */
+ @SuppressWarnings("unchecked")
+ public void addNestedContainerBean(String propertyId,
+ boolean nullBeansAllowed) {
Class<?> propertyType = getType(propertyId);
LinkedHashMap<String, VaadinPropertyDescriptor<Object>> pds = BeanItem
.getPropertyDescriptors((Class<Object>) propertyType);
for (String subPropertyId : pds.keySet()) {
String qualifiedPropertyId = propertyId + "." + subPropertyId;
NestedPropertyDescriptor<BEANTYPE> pd = new NestedPropertyDescriptor<BEANTYPE>(
- qualifiedPropertyId, (Class<BEANTYPE>) type);
+ qualifiedPropertyId, (Class<BEANTYPE>) type,
+ nullBeansAllowed);
model.put(qualifiedPropertyId, pd);
model.remove(propertyId);
for (BeanItem<BEANTYPE> item : itemIdToItem.values()) {
diff --git a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
index 84304431bc..9a7922b928 100644
--- a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
+++ b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java
@@ -15,8 +15,10 @@
*/
package com.vaadin.data.util;
+import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
+import java.util.EventObject;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
@@ -146,6 +148,85 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
}
}
+ private static abstract class BaseItemAddOrRemoveEvent extends
+ EventObject implements Serializable {
+ protected Object itemId;
+ protected int index;
+ protected int count;
+
+ public BaseItemAddOrRemoveEvent(Container source, Object itemId,
+ int index, int count) {
+ super(source);
+ this.itemId = itemId;
+ this.index = index;
+ this.count = count;
+ }
+
+ public Container getContainer() {
+ return (Container) getSource();
+ }
+
+ public Object getFirstItemId() {
+ return itemId;
+ }
+
+ public int getFirstIndex() {
+ return index;
+ }
+
+ public int getAffectedItemsCount() {
+ return count;
+ }
+ }
+
+ /**
+ * An <code>Event</code> object specifying information about the added
+ * items.
+ *
+ * <p>
+ * This class provides information about the first added item and the number
+ * of added items.
+ * </p>
+ */
+ protected static class BaseItemAddEvent extends
+ BaseItemAddOrRemoveEvent implements
+ Container.Indexed.ItemAddEvent {
+
+ public BaseItemAddEvent(Container source, Object itemId, int index,
+ int count) {
+ super(source, itemId, index, count);
+ }
+
+ @Override
+ public int getAddedItemsCount() {
+ return getAffectedItemsCount();
+ }
+ }
+
+ /**
+ * An <code>Event</code> object specifying information about the removed
+ * items.
+ *
+ * <p>
+ * This class provides information about the first removed item and the
+ * number of removed items.
+ * </p>
+ */
+ protected static class BaseItemRemoveEvent extends
+ BaseItemAddOrRemoveEvent implements
+ Container.Indexed.ItemRemoveEvent {
+
+ public BaseItemRemoveEvent(Container source, Object itemId,
+ int index, int count) {
+ super(source, itemId, index, count);
+ }
+
+ @Override
+ public int getRemovedItemsCount() {
+ return getAffectedItemsCount();
+ }
+ }
+
/**
* Get an item even if filtered out.
*
@@ -898,36 +979,69 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
* Notify item set change listeners that an item has been added to the
* container.
*
- * Unless subclasses specify otherwise, the default notification indicates a
- * full refresh.
- *
* @param postion
- * position of the added item in the view (if visible)
+ * position of the added item in the view
* @param itemId
* id of the added item
* @param item
* the added item
*/
protected void fireItemAdded(int position, ITEMIDTYPE itemId, ITEMCLASS item) {
- fireItemSetChange();
+ fireItemsAdded(position, itemId, 1);
+ }
+
+ /**
+ * Notify item set change listeners that items has been added to the
+ * container.
+ *
+ * @param firstPosition
+ * position of the first visible added item in the view
+ * @param firstItemId
+ * id of the first visible added item
+ * @param numberOfItems
+ * the number of visible added items
+ */
+ protected void fireItemsAdded(int firstPosition, ITEMIDTYPE firstItemId,
+ int numberOfItems) {
+ BaseItemAddEvent addEvent = new BaseItemAddEvent(this,
+ firstItemId, firstPosition, numberOfItems);
+ fireItemSetChange(addEvent);
}
/**
* Notify item set change listeners that an item has been removed from the
* container.
*
- * Unless subclasses specify otherwise, the default notification indicates a
- * full refresh.
+ * @param position
+ * position of the removed item in the view prior to removal
*
- * @param postion
- * position of the removed item in the view prior to removal (if
- * was visible)
* @param itemId
* id of the removed item, of type {@link Object} to satisfy
* {@link Container#removeItem(Object)} API
*/
protected void fireItemRemoved(int position, Object itemId) {
- fireItemSetChange();
+ fireItemsRemoved(position, itemId, 1);
+ }
+
+ /**
+ * Notify item set change listeners that items has been removed from the
+ * container.
+ *
+ * @param firstPosition
+ * position of the first visible removed item in the view prior
+ * to removal
+ * @param firstItemId
+ * id of the first visible removed item, of type {@link Object}
+ * to satisfy {@link Container#removeItem(Object)} API
+ * @param numberOfItems
+ * the number of removed visible items
+ *
+ */
+ protected void fireItemsRemoved(int firstPosition, Object firstItemId,
+ int numberOfItems) {
+ BaseItemRemoveEvent removeEvent = new BaseItemRemoveEvent(this,
+ firstItemId, firstPosition, numberOfItems);
+ fireItemSetChange(removeEvent);
}
// visible and filtered item identifier lists
@@ -946,6 +1060,21 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE
}
/**
+ * Returns the item id of the first visible item after filtering. 'Null' is
+ * returned if there is no visible items.
+ *
+ * For internal use only.
+ *
+ * @return item id of the first visible item
+ */
+ protected ITEMIDTYPE getFirstVisibleItem() {
+ if (!getVisibleItemIds().isEmpty()) {
+ return getVisibleItemIds().get(0);
+ }
+ return null;
+ }
+
+ /**
* Returns true is the container has active filters.
*
* @return true if the container is currently filtered
diff --git a/server/src/com/vaadin/data/util/BeanItem.java b/server/src/com/vaadin/data/util/BeanItem.java
index fc51be8f36..4834fe4f89 100644
--- a/server/src/com/vaadin/data/util/BeanItem.java
+++ b/server/src/com/vaadin/data/util/BeanItem.java
@@ -268,6 +268,27 @@ public class BeanItem<BT> extends PropertysetItem {
}
/**
+ * Adds a nested property to the item. If the <code>nullBeansAllowed</code>
+ * flag is set to true, calling getValue of the added property will return
+ * null if the property or any of its intermediate getters returns null. If
+ * set to false, null values returned by intermediate getters will cause
+ * NullPointerException. The default value is false to ensure backwards
+ * compatibility.
+ *
+ * @param nestedPropertyId
+ * property id to add. This property must not exist in the item
+ * already and must of of form "field1.field2" where field2 is a
+ * field in the object referenced to by field1
+ * @param nullBeansAllowed
+ * set true to allow null values from intermediate getters
+ */
+ public void addNestedProperty(String nestedPropertyId,
+ boolean nullBeansAllowed) {
+ addItemProperty(nestedPropertyId, new NestedMethodProperty<Object>(
+ getBean(), nestedPropertyId, nullBeansAllowed));
+ }
+
+ /**
* Gets the underlying JavaBean object.
*
* @return the bean object.
diff --git a/server/src/com/vaadin/data/util/IndexedContainer.java b/server/src/com/vaadin/data/util/IndexedContainer.java
index d7bf70caf6..5d20919208 100644
--- a/server/src/com/vaadin/data/util/IndexedContainer.java
+++ b/server/src/com/vaadin/data/util/IndexedContainer.java
@@ -226,6 +226,7 @@ public class IndexedContainer extends
@Override
public boolean removeAllItems() {
int origSize = size();
+ Object firstItem = getFirstVisibleItem();
internalRemoveAllItems();
@@ -235,7 +236,7 @@ public class IndexedContainer extends
// filtered out items were removed or not
if (origSize != 0) {
// Sends a change event
- fireItemSetChange();
+ fireItemsRemoved(0, firstItem, origSize);
}
return true;
@@ -620,8 +621,7 @@ public class IndexedContainer extends
@Override
protected void fireItemAdded(int position, Object itemId, Item item) {
if (position >= 0) {
- fireItemSetChange(new IndexedContainer.ItemSetChangeEvent(this,
- position));
+ super.fireItemAdded(position, itemId, item);
}
}
@@ -1211,4 +1211,5 @@ public class IndexedContainer extends
public Collection<Filter> getContainerFilters() {
return super.getContainerFilters();
}
+
}
diff --git a/server/src/com/vaadin/data/util/NestedMethodProperty.java b/server/src/com/vaadin/data/util/NestedMethodProperty.java
index b62ecfbfc3..7a3963c17e 100644
--- a/server/src/com/vaadin/data/util/NestedMethodProperty.java
+++ b/server/src/com/vaadin/data/util/NestedMethodProperty.java
@@ -32,7 +32,7 @@ import com.vaadin.data.util.MethodProperty.MethodException;
* can contain multiple levels of nesting.
*
* When accessing the property value, all intermediate getters must return
- * non-null values.
+ * non-null values or the <code>nullBeansAllowed</code> must be set to true.
*
* @see MethodProperty
*
@@ -55,6 +55,15 @@ public class NestedMethodProperty<T> extends AbstractProperty<T> {
*/
private Object instance;
+ /**
+ * a boolean flag indicating whether intermediate getters may return null
+ * values. If the flag is set to true, calling getValue will return null if
+ * the property or any of its intermediate getters returns null. If set to
+ * false, intermediate getters returning null value will throw Exception.
+ * The default value is false to ensure backwards compatibility.
+ */
+ private boolean nullBeansAllowed = false;
+
private Class<? extends T> type;
/* Special serialization to handle method references */
@@ -85,7 +94,33 @@ public class NestedMethodProperty<T> extends AbstractProperty<T> {
* if the property name is invalid
*/
public NestedMethodProperty(Object instance, String propertyName) {
+ this(instance, propertyName, false);
+ }
+
+ /**
+ * Constructs a nested method property for a given object instance. The
+ * property name is a dot separated string pointing to a nested property,
+ * e.g. "manager.address.street". The <code>nullBeansAllowed</code> controls
+ * the behavior in cases where the intermediate getters may return null
+ * values. If the flag is set to true, calling getValue will return null if
+ * the property or any of its intermediate getters returns null. If set to
+ * false, null values returned by intermediate getters will cause
+ * NullPointerException. The default value is false to ensure backwards
+ * compatibility.
+ *
+ * @param instance
+ * top-level bean to which the property applies
+ * @param propertyName
+ * dot separated nested property name
+ * @param nullBeansAllowed
+ * set true to allow null values from intermediate getters
+ * @throws IllegalArgumentException
+ * if the property name is invalid
+ */
+ public NestedMethodProperty(Object instance, String propertyName,
+ boolean nullBeansAllowed) {
this.instance = instance;
+ this.nullBeansAllowed = nullBeansAllowed;
initialize(instance.getClass(), propertyName);
}
@@ -104,6 +139,25 @@ public class NestedMethodProperty<T> extends AbstractProperty<T> {
}
/**
+ * For internal use to deduce property type etc. without a bean instance.
+ * Calling {@link #setValue(Object)} or {@link #getValue()} on properties
+ * constructed this way is not supported.
+ *
+ * @param instanceClass
+ * class of the top-level bean
+ * @param propertyName
+ * dot separated nested property name
+ * @param nullBeansAllowed
+ * set true to allow null values from intermediate getters
+ */
+ NestedMethodProperty(Class<?> instanceClass, String propertyName,
+ boolean nullBeansAllowed) {
+ instance = null;
+ this.nullBeansAllowed = nullBeansAllowed;
+ initialize(instanceClass, propertyName);
+ }
+
+ /**
* Initializes most of the internal fields based on the top-level bean
* instance and property name (dot-separated string).
*
@@ -199,6 +253,9 @@ public class NestedMethodProperty<T> extends AbstractProperty<T> {
Object object = instance;
for (Method m : getMethods) {
object = m.invoke(object);
+ if (object == null && nullBeansAllowed) {
+ return null;
+ }
}
return (T) object;
} catch (final Throwable e) {
diff --git a/server/src/com/vaadin/data/util/NestedPropertyDescriptor.java b/server/src/com/vaadin/data/util/NestedPropertyDescriptor.java
index b2055fe776..67eb30fae5 100644
--- a/server/src/com/vaadin/data/util/NestedPropertyDescriptor.java
+++ b/server/src/com/vaadin/data/util/NestedPropertyDescriptor.java
@@ -34,6 +34,7 @@ public class NestedPropertyDescriptor<BT> implements
private final String name;
private final Class<?> propertyType;
+ private final boolean nullBeansAllowed;
/**
* Creates a property descriptor that can create MethodProperty instances to
@@ -48,10 +49,29 @@ public class NestedPropertyDescriptor<BT> implements
*/
public NestedPropertyDescriptor(String name, Class<BT> beanType)
throws IllegalArgumentException {
+ this(name, beanType, false);
+ }
+
+ /**
+ * Creates a property descriptor that can create MethodProperty instances to
+ * access the underlying bean property.
+ *
+ * @param name
+ * of the property in a dotted path format, e.g. "address.street"
+ * @param beanType
+ * type (class) of the top-level bean
+ * @param nullBeansAllowed
+ * set true to allow null values from intermediate getters
+ * @throws IllegalArgumentException
+ * if the property name is invalid
+ */
+ public NestedPropertyDescriptor(String name, Class<BT> beanType,
+ boolean nullBeansAllowed) throws IllegalArgumentException {
this.name = name;
NestedMethodProperty<?> property = new NestedMethodProperty<Object>(
- beanType, name);
+ beanType, name, nullBeansAllowed);
this.propertyType = property.getType();
+ this.nullBeansAllowed = nullBeansAllowed;
}
@Override
@@ -66,7 +86,7 @@ public class NestedPropertyDescriptor<BT> implements
@Override
public Property<?> createProperty(BT bean) {
- return new NestedMethodProperty<Object>(bean, name);
+ return new NestedMethodProperty<Object>(bean, name, nullBeansAllowed);
}
}
diff --git a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
index bbd3945a37..4d3717e9ba 100644
--- a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
+++ b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
@@ -16,6 +16,7 @@
package com.vaadin.data.util.converter;
+import java.math.BigDecimal;
import java.util.Date;
import java.util.logging.Logger;
@@ -101,10 +102,12 @@ public class DefaultConverterFactory implements ConverterFactory {
return new StringToFloatConverter();
} else if (Integer.class.isAssignableFrom(sourceType)) {
return new StringToIntegerConverter();
+ } else if (Long.class.isAssignableFrom(sourceType)) {
+ return new StringToLongConverter();
+ } else if (BigDecimal.class.isAssignableFrom(sourceType)) {
+ return new StringToBigDecimalConverter();
} else if (Boolean.class.isAssignableFrom(sourceType)) {
return new StringToBooleanConverter();
- } else if (Number.class.isAssignableFrom(sourceType)) {
- return new StringToNumberConverter();
} else if (Date.class.isAssignableFrom(sourceType)) {
return new StringToDateConverter();
} else {
diff --git a/server/src/com/vaadin/data/util/converter/StringToBigDecimalConverter.java b/server/src/com/vaadin/data/util/converter/StringToBigDecimalConverter.java
new file mode 100644
index 0000000000..75d4cedd23
--- /dev/null
+++ b/server/src/com/vaadin/data/util/converter/StringToBigDecimalConverter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.data.util.converter;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+/**
+ * A converter that converts from {@link String} to {@link BigDecimal} and back.
+ * Uses the given locale and a {@link NumberFormat} instance for formatting and
+ * parsing.
+ * <p>
+ * Leading and trailing white spaces are ignored when converting from a String.
+ * </p>
+ * <p>
+ * Override and overwrite {@link #getFormat(Locale)} to use a different format.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @since 7.2
+ */
+public class StringToBigDecimalConverter extends
+ AbstractStringToNumberConverter<BigDecimal> {
+ @Override
+ protected NumberFormat getFormat(Locale locale) {
+ NumberFormat numberFormat = super.getFormat(locale);
+ if (numberFormat instanceof DecimalFormat) {
+ ((DecimalFormat) numberFormat).setParseBigDecimal(true);
+ }
+
+ return numberFormat;
+ }
+
+ @Override
+ public BigDecimal convertToModel(String value,
+ Class<? extends BigDecimal> targetType, Locale locale)
+ throws com.vaadin.data.util.converter.Converter.ConversionException {
+ return (BigDecimal) convertToNumber(value, BigDecimal.class, locale);
+ }
+
+ @Override
+ public Class<BigDecimal> getModelType() {
+ return BigDecimal.class;
+ }
+}
diff --git a/server/src/com/vaadin/data/util/converter/StringToNumberConverter.java b/server/src/com/vaadin/data/util/converter/StringToLongConverter.java
index 22df42403f..3532336787 100644
--- a/server/src/com/vaadin/data/util/converter/StringToNumberConverter.java
+++ b/server/src/com/vaadin/data/util/converter/StringToLongConverter.java
@@ -20,17 +20,35 @@ import java.text.NumberFormat;
import java.util.Locale;
/**
- * A converter that converts from {@link Number} to {@link String} and back.
- * Uses the given locale and {@link NumberFormat} for formatting and parsing.
+ * A converter that converts from {@link String} to {@link Long} and back. Uses
+ * the given locale and a {@link NumberFormat} instance for formatting and
+ * parsing.
* <p>
* Override and overwrite {@link #getFormat(Locale)} to use a different format.
* </p>
*
* @author Vaadin Ltd
- * @since 7.0
+ * @since 7.2
*/
-public class StringToNumberConverter extends
- AbstractStringToNumberConverter<Number> {
+public class StringToLongConverter extends
+ AbstractStringToNumberConverter<Long> {
+
+ /**
+ * Returns the format used by
+ * {@link #convertToPresentation(Long, Class, Locale)} and
+ * {@link #convertToModel(String, Class, Locale)}
+ *
+ * @param locale
+ * The locale to use
+ * @return A NumberFormat instance
+ */
+ @Override
+ protected NumberFormat getFormat(Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ return NumberFormat.getIntegerInstance(locale);
+ }
/*
* (non-Javadoc)
@@ -40,16 +58,11 @@ public class StringToNumberConverter extends
* java.lang.Class, java.util.Locale)
*/
@Override
- public Number convertToModel(String value,
- Class<? extends Number> targetType, Locale locale)
- throws ConversionException {
- if (targetType != getModelType()) {
- throw new ConversionException("Converter only supports "
- + getModelType().getName() + " (targetType was "
- + targetType.getName() + ")");
- }
+ public Long convertToModel(String value, Class<? extends Long> targetType,
+ Locale locale) throws ConversionException {
+ Number n = convertToNumber(value, targetType, locale);
+ return n == null ? null : n.longValue();
- return convertToNumber(value, targetType, locale);
}
/*
@@ -58,8 +71,8 @@ public class StringToNumberConverter extends
* @see com.vaadin.data.util.converter.Converter#getModelType()
*/
@Override
- public Class<Number> getModelType() {
- return Number.class;
+ public Class<Long> getModelType() {
+ return Long.class;
}
}
diff --git a/server/src/com/vaadin/data/validator/BeanValidator.java b/server/src/com/vaadin/data/validator/BeanValidator.java
index ea7189bc5e..54efa51ac1 100644
--- a/server/src/com/vaadin/data/validator/BeanValidator.java
+++ b/server/src/com/vaadin/data/validator/BeanValidator.java
@@ -17,8 +17,6 @@
package com.vaadin.data.validator;
import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -115,7 +113,9 @@ public class BeanValidator implements Validator {
Set<?> violations = getJavaxBeanValidator().validateValue(beanClass,
propertyName, value);
if (violations.size() > 0) {
- List<String> exceptions = new ArrayList<String>();
+ InvalidValueException[] causes = new InvalidValueException[violations
+ .size()];
+ int i = 0;
for (Object v : violations) {
final ConstraintViolation<?> violation = (ConstraintViolation<?>) v;
String msg = getJavaxBeanValidatorFactory()
@@ -123,16 +123,11 @@ public class BeanValidator implements Validator {
violation.getMessageTemplate(),
new SimpleContext(value, violation
.getConstraintDescriptor()), locale);
- exceptions.add(msg);
+ causes[i] = new InvalidValueException(msg);
+ ++i;
}
- StringBuilder b = new StringBuilder();
- for (int i = 0; i < exceptions.size(); i++) {
- if (i != 0) {
- b.append("<br/>");
- }
- b.append(exceptions.get(i));
- }
- throw new InvalidValueException(b.toString());
+
+ throw new InvalidValueException(null, causes);
}
}
diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java
index 0a4949ffa7..5a117958a0 100644
--- a/server/src/com/vaadin/server/BootstrapHandler.java
+++ b/server/src/com/vaadin/server/BootstrapHandler.java
@@ -146,14 +146,15 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
}
@Override
+ protected boolean canHandleRequest(VaadinRequest request) {
+ // We do not want to handle /APP requests here, instead let it fall
+ // through and produce a 404
+ return !ServletPortletHelper.isAppRequest(request);
+ }
+
+ @Override
public boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response) throws IOException {
- if (ServletPortletHelper.isAppRequest(request)) {
- // We do not want to handle /APP requests here, instead let it fall
- // through and produce a 404
- return false;
- }
-
try {
// Update WebBrowser here only to make WebBrowser information
// available in init for LegacyApplications
diff --git a/server/src/com/vaadin/server/Page.java b/server/src/com/vaadin/server/Page.java
index 037d8e8352..5c8b1aeb42 100644
--- a/server/src/com/vaadin/server/Page.java
+++ b/server/src/com/vaadin/server/Page.java
@@ -476,6 +476,8 @@ public class Page implements Serializable {
private final PageState state;
+ private String windowName;
+
public Page(UI uI, PageState state) {
this.uI = uI;
this.state = state;
@@ -637,6 +639,7 @@ public class Page implements Serializable {
String location = request.getParameter("v-loc");
String clientWidth = request.getParameter("v-cw");
String clientHeight = request.getParameter("v-ch");
+ windowName = request.getParameter("v-wn");
if (location != null) {
try {
@@ -662,6 +665,17 @@ public class Page implements Serializable {
}
/**
+ * Gets the window.name value of the browser window of this page.
+ *
+ * @since 7.2
+ *
+ * @return the window name, <code>null</code> if the name is not known
+ */
+ public String getWindowName() {
+ return windowName;
+ }
+
+ /**
* Updates the internal state with the given values. Does not resize the
* Page or browser window.
*
@@ -1124,7 +1138,7 @@ public class Page implements Serializable {
* the new page title to set
*/
public void setTitle(String title) {
- uI.getRpcProxy(PageClientRpc.class).setTitle(title);
+ getState(true).title = title;
}
/**
diff --git a/server/src/com/vaadin/server/ServiceDestroyEvent.java b/server/src/com/vaadin/server/ServiceDestroyEvent.java
new file mode 100644
index 0000000000..2ae4cc10af
--- /dev/null
+++ b/server/src/com/vaadin/server/ServiceDestroyEvent.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.server;
+
+import java.util.EventObject;
+
+/**
+ * Event fired to {@link ServiceDestroyListener} when a {@link VaadinService} is
+ * being destroyed.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class ServiceDestroyEvent extends EventObject {
+
+ /**
+ * Creates a new event for the given service.
+ *
+ * @param service
+ * the service being destroyed
+ */
+ public ServiceDestroyEvent(VaadinService service) {
+ super(service);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.EventObject#getSource()
+ */
+ @Override
+ public VaadinService getSource() {
+ return (VaadinService) super.getSource();
+ }
+
+}
diff --git a/server/src/com/vaadin/server/ServiceDestroyListener.java b/server/src/com/vaadin/server/ServiceDestroyListener.java
new file mode 100644
index 0000000000..ad4966dd58
--- /dev/null
+++ b/server/src/com/vaadin/server/ServiceDestroyListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.server;
+
+import java.io.Serializable;
+
+/**
+ * Listener that gets notified when the {@link VaadinService} to which it has
+ * been registered is destroyed.
+ *
+ * @see VaadinService#addServiceDestroyListener(ServiceDestroyListener)
+ * @see VaadinService#removeServiceDestroyListener(ServiceDestroyListener)
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public interface ServiceDestroyListener extends Serializable {
+ /**
+ * Invoked when a service is destroyed
+ *
+ * @param event
+ * the event
+ */
+ public void serviceDestroy(ServiceDestroyEvent event);
+}
diff --git a/server/src/com/vaadin/server/SynchronizedRequestHandler.java b/server/src/com/vaadin/server/SynchronizedRequestHandler.java
index ac730dcecb..c695855d7d 100644
--- a/server/src/com/vaadin/server/SynchronizedRequestHandler.java
+++ b/server/src/com/vaadin/server/SynchronizedRequestHandler.java
@@ -32,6 +32,10 @@ public abstract class SynchronizedRequestHandler implements RequestHandler {
@Override
public boolean handleRequest(VaadinSession session, VaadinRequest request,
VaadinResponse response) throws IOException {
+ if (!canHandleRequest(request)) {
+ return false;
+ }
+
session.lock();
try {
return synchronizedHandleRequest(session, request, response);
@@ -62,4 +66,25 @@ public abstract class SynchronizedRequestHandler implements RequestHandler {
public abstract boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response) throws IOException;
+ /**
+ * Check whether a request may be handled by this handler. This can be used
+ * as an optimization to avoid locking the session just to investigate some
+ * method property. The default implementation just returns
+ * <code>true</code> which means that all requests will be handled by
+ * calling
+ * {@link #synchronizedHandleRequest(VaadinSession, VaadinRequest, VaadinResponse)}
+ * with the session locked.
+ *
+ * @since 7.2
+ * @param request
+ * the request to handle
+ * @return <code>true</code> if the request handling should continue once
+ * the session has been locked; <code>false</code> if there's no
+ * need to lock the session since the request would still not be
+ * handled.
+ */
+ protected boolean canHandleRequest(VaadinRequest request) {
+ return true;
+ }
+
}
diff --git a/server/src/com/vaadin/server/SystemMessages.java b/server/src/com/vaadin/server/SystemMessages.java
index 5e0fde1d4a..299c725207 100644
--- a/server/src/com/vaadin/server/SystemMessages.java
+++ b/server/src/com/vaadin/server/SystemMessages.java
@@ -63,32 +63,32 @@ public class SystemMessages implements Serializable {
protected String sessionExpiredURL = null;
protected boolean sessionExpiredNotificationEnabled = true;
protected String sessionExpiredCaption = "Session Expired";
- protected String sessionExpiredMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
+ protected String sessionExpiredMessage = "Take note of any unsaved data, and <u>click here</u> or press ESC key to continue.";
protected String communicationErrorURL = null;
protected boolean communicationErrorNotificationEnabled = true;
protected String communicationErrorCaption = "Communication problem";
- protected String communicationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
+ protected String communicationErrorMessage = "Take note of any unsaved data, and <u>click here</u> or press ESC to continue.";
protected String authenticationErrorURL = null;
protected boolean authenticationErrorNotificationEnabled = true;
protected String authenticationErrorCaption = "Authentication problem";
- protected String authenticationErrorMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
+ protected String authenticationErrorMessage = "Take note of any unsaved data, and <u>click here</u> or press ESC to continue.";
protected String internalErrorURL = null;
protected boolean internalErrorNotificationEnabled = true;
protected String internalErrorCaption = "Internal error";
- protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> to continue.";
+ protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> or press ESC to continue.";
protected String outOfSyncURL = null;
protected boolean outOfSyncNotificationEnabled = true;
protected String outOfSyncCaption = "Out of sync";
- protected String outOfSyncMessage = "Something has caused us to be out of sync with the server.<br/>Take note of any unsaved data, and <u>click here</u> to re-sync.";
+ protected String outOfSyncMessage = "Something has caused us to be out of sync with the server.<br/>Take note of any unsaved data, and <u>click here</u> or press ESC to re-sync.";
protected String cookiesDisabledURL = null;
protected boolean cookiesDisabledNotificationEnabled = true;
protected String cookiesDisabledCaption = "Cookies disabled";
- protected String cookiesDisabledMessage = "This application requires cookies to function.<br/>Please enable cookies in your browser and <u>click here</u> to try again.";
+ protected String cookiesDisabledMessage = "This application requires cookies to function.<br/>Please enable cookies in your browser and <u>click here</u> or press ESC to try again.";
/**
* Use {@link CustomizedSystemMessages} to customize
diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java
index 093a1c9152..a41f301219 100644
--- a/server/src/com/vaadin/server/VaadinPortlet.java
+++ b/server/src/com/vaadin/server/VaadinPortlet.java
@@ -503,6 +503,12 @@ public class VaadinPortlet extends GenericPortlet implements Constants,
handleRequest(request, response);
}
+ @Override
+ public void destroy() {
+ super.destroy();
+ getService().destroy();
+ }
+
private static final Logger getLogger() {
return Logger.getLogger(VaadinPortlet.class.getName());
}
diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java
index 44ceaaaf87..aff0124d16 100644
--- a/server/src/com/vaadin/server/VaadinService.java
+++ b/server/src/com/vaadin/server/VaadinService.java
@@ -41,7 +41,9 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.portlet.Portlet;
import javax.portlet.PortletContext;
+import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
@@ -97,6 +99,10 @@ public abstract class VaadinService implements Serializable {
.findMethod(SessionDestroyListener.class, "sessionDestroy",
SessionDestroyEvent.class);
+ private static final Method SERVICE_DESTROY_METHOD = ReflectTools
+ .findMethod(ServiceDestroyListener.class, "serviceDestroy",
+ ServiceDestroyEvent.class);
+
/**
* @deprecated As of 7.0. Only supported for {@link LegacyApplication}.
*/
@@ -1674,23 +1680,6 @@ public abstract class VaadinService implements Serializable {
FutureAccess future = new FutureAccess(session, runnable);
session.getPendingAccessQueue().add(future);
- ensureAccessQueuePurged(session);
-
- return future;
- }
-
- /**
- * Makes sure the pending access queue is purged for the provided session.
- * If the session is currently locked by the current thread or some other
- * thread, the queue will be purged when the session is unlocked. If the
- * lock is not held by any thread, it is acquired and the queue is purged
- * right away.
- *
- * @since 7.1.2
- * @param session
- * the session for which the access queue should be purged
- */
- public void ensureAccessQueuePurged(VaadinSession session) {
/*
* If no thread is currently holding the lock, pending changes for UIs
* with automatic push would not be processed and pushed until the next
@@ -1713,6 +1702,8 @@ public abstract class VaadinService implements Serializable {
} catch (InterruptedException e) {
// Just ignore
}
+
+ return future;
}
/**
@@ -1759,4 +1750,50 @@ public abstract class VaadinService implements Serializable {
CurrentInstance.restoreInstances(oldInstances);
}
}
+
+ /**
+ * Adds a service destroy listener that gets notified when this service is
+ * destroyed.
+ *
+ * @since 7.2
+ * @param listener
+ * the service destroy listener to add
+ *
+ * @see #destroy()
+ * @see #removeServiceDestroyListener(ServiceDestroyListener)
+ * @see ServiceDestroyListener
+ */
+ public void addServiceDestroyListener(ServiceDestroyListener listener) {
+ eventRouter.addListener(ServiceDestroyEvent.class, listener,
+ SERVICE_DESTROY_METHOD);
+ }
+
+ /**
+ * Removes a service destroy listener that was previously added with
+ * {@link #addServiceDestroyListener(ServiceDestroyListener)}.
+ *
+ * @since 7.2
+ * @param listener
+ * the service destroy listener to remove
+ */
+ public void removeServiceDestroyListener(ServiceDestroyListener listener) {
+ eventRouter.removeListener(ServiceDestroyEvent.class, listener,
+ SERVICE_DESTROY_METHOD);
+ }
+
+ /**
+ * Called when the servlet, portlet or similar for this service is being
+ * destroyed. After this method has been called, no more requests will be
+ * handled by this service.
+ *
+ * @see #addServiceDestroyListener(ServiceDestroyListener)
+ * @see Servlet#destroy()
+ * @see Portlet#destroy()
+ *
+ * @since 7.2
+ */
+ public void destroy() {
+ eventRouter.fireEvent(new ServiceDestroyEvent(this));
+ }
+
}
diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java
index c16be33de2..d34cd3bf0e 100644
--- a/server/src/com/vaadin/server/VaadinServlet.java
+++ b/server/src/com/vaadin/server/VaadinServlet.java
@@ -43,7 +43,6 @@ import javax.servlet.http.HttpServletResponse;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.annotations.VaadinServletConfiguration.InitParameterName;
import com.vaadin.sass.internal.ScssStylesheet;
-import com.vaadin.server.communication.PushRequestHandler;
import com.vaadin.server.communication.ServletUIInitHandler;
import com.vaadin.shared.JsonConstants;
import com.vaadin.ui.UI;
@@ -672,21 +671,11 @@ public class VaadinServlet extends HttpServlet implements Constants {
// Provide modification timestamp to the browser if it is known.
if (lastModifiedTime > 0) {
response.setDateHeader("Last-Modified", lastModifiedTime);
- /*
- * The browser is allowed to cache for 1 hour without checking if
- * the file has changed. This forces browsers to fetch a new version
- * when the Vaadin version is updated. This will cause more requests
- * to the servlet than without this but for high volume sites the
- * static files should never be served through the servlet. The
- * cache timeout can be configured by setting the resourceCacheTime
- * parameter in web.xml
- */
- int resourceCacheTime = getService().getDeploymentConfiguration()
- .getResourceCacheTime();
- String cacheControl = "max-age="
- + String.valueOf(resourceCacheTime);
- if (filename.contains("nocache")) {
- cacheControl = "public, max-age=0, must-revalidate";
+
+ String cacheControl = "public, max-age=0, must-revalidate";
+ int resourceCacheTime = getCacheTime(filename);
+ if (resourceCacheTime > 0) {
+ cacheControl = "max-age=" + String.valueOf(resourceCacheTime);
}
response.setHeader("Cache-Control", cacheControl);
}
@@ -695,6 +684,43 @@ public class VaadinServlet extends HttpServlet implements Constants {
}
/**
+ * Calculates the cache lifetime for the given filename in seconds. By
+ * default filenames containing ".nocache." return 0, filenames containing
+ * ".cache." return one year, all other return the value defined in the
+ * web.xml using resourceCacheTime (defaults to 1 hour).
+ *
+ * @param filename
+ * @return cache lifetime for the given filename in seconds
+ */
+ protected int getCacheTime(String filename) {
+ /*
+ * GWT conventions:
+ *
+ * - files containing .nocache. will not be cached.
+ *
+ * - files containing .cache. will be cached for one year.
+ *
+ * https://developers.google.com/web-toolkit/doc/latest/
+ * DevGuideCompilingAndDebugging#perfect_caching
+ */
+ if (filename.contains(".nocache.")) {
+ return 0;
+ }
+ if (filename.contains(".cache.")) {
+ return 60 * 60 * 24 * 365;
+ }
+ /*
+ * For all other files, the browser is allowed to cache for 1 hour
+ * without checking if the file has changed. This forces browsers to
+ * fetch a new version when the Vaadin version is updated. This will
+ * cause more requests to the servlet than without this but for high
+ * volume sites the static files should never be served through the
+ * servlet.
+ */
+ return getService().getDeploymentConfiguration().getResourceCacheTime();
+ }
+
+ /**
* Writes the contents of the given resourceUrl in the response. Can be
* overridden to add/modify response headers and similar.
*
@@ -984,20 +1010,8 @@ public class VaadinServlet extends HttpServlet implements Constants {
}
protected boolean isStaticResourceRequest(HttpServletRequest request) {
- String pathInfo = request.getPathInfo();
- if (pathInfo == null) {
- return false;
- }
-
- if ((request.getContextPath() != null)
- && (request.getRequestURI().startsWith("/VAADIN/"))) {
- return true;
- } else if (request.getRequestURI().startsWith(
- request.getContextPath() + "/VAADIN/")) {
- return true;
- }
-
- return false;
+ return request.getRequestURI().startsWith(
+ request.getContextPath() + "/VAADIN/");
}
/**
@@ -1077,15 +1091,15 @@ public class VaadinServlet extends HttpServlet implements Constants {
return u;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.GenericServlet#destroy()
+ */
@Override
public void destroy() {
super.destroy();
-
- for (RequestHandler handler : getService().getRequestHandlers()) {
- if (handler instanceof PushRequestHandler) {
- ((PushRequestHandler) handler).destroy();
- }
- }
+ getService().destroy();
}
/**
diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java
index fd2ed79acd..f34721944a 100644
--- a/server/src/com/vaadin/server/VaadinSession.java
+++ b/server/src/com/vaadin/server/VaadinSession.java
@@ -43,7 +43,6 @@ import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
-import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.data.util.converter.ConverterFactory;
import com.vaadin.data.util.converter.DefaultConverterFactory;
@@ -205,7 +204,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
private int nextUIId = 0;
private Map<Integer, UI> uIs = new HashMap<Integer, UI>();
- private final Map<String, Integer> retainOnRefreshUIs = new HashMap<String, Integer>();
+ private final Map<String, Integer> embedIdMap = new HashMap<String, Integer>();
private final EventRouter eventRouter = new EventRouter();
@@ -828,10 +827,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
*/
public void removeUI(UI ui) {
assert hasLock();
- int id = ui.getUIId();
+ Integer id = Integer.valueOf(ui.getUIId());
ui.setSession(null);
uIs.remove(id);
- retainOnRefreshUIs.values().remove(id);
+ String embedId = ui.getEmbedId();
+ if (embedId != null && id.equals(embedIdMap.get(embedId))) {
+ embedIdMap.remove(embedId);
+ }
}
/**
@@ -938,14 +940,12 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
*/
public void unlock() {
assert hasLock();
- boolean ultimateRelease = false;
try {
/*
* Run pending tasks and push if the reentrant lock will actually be
* released by this unlock() invocation.
*/
if (((ReentrantLock) getLockInstance()).getHoldCount() == 1) {
- ultimateRelease = true;
getService().runPendingAccessTasks(this);
for (UI ui : getUIs()) {
@@ -963,18 +963,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
} finally {
getLockInstance().unlock();
}
-
- /*
- * If the session is locked when a new access task is added, it is
- * assumed that the queue will be purged when the lock is released. This
- * might however not happen if a task is enqueued between the moment
- * when unlock() purges the queue and the moment when the lock is
- * actually released. This means that the queue should be purged again
- * if it is not empty after unlocking.
- */
- if (ultimateRelease && !getPendingAccessQueue().isEmpty()) {
- getService().ensureAccessQueuePurged(this);
- }
}
/**
@@ -1099,20 +1087,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
}
/**
- * Gets the mapping from <code>window.name</code> to UI id for UIs that are
- * should be retained on refresh.
- *
- * @see VaadinService#preserveUIOnRefresh(VaadinRequest, UI, UIProvider)
- * @see PreserveOnRefresh
- *
- * @return the mapping between window names and UI ids for this session.
- */
- public Map<String, Integer> getPreserveOnRefreshUIs() {
- assert hasLock();
- return retainOnRefreshUIs;
- }
-
- /**
* Adds an initialized UI to this session.
*
* @param ui
@@ -1129,7 +1103,21 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
"The UI belongs to a different session");
}
- uIs.put(Integer.valueOf(ui.getUIId()), ui);
+ Integer uiId = Integer.valueOf(ui.getUIId());
+ uIs.put(uiId, ui);
+
+ String embedId = ui.getEmbedId();
+ if (embedId != null) {
+ Integer previousUiId = embedIdMap.put(embedId, uiId);
+ if (previousUiId != null) {
+ UI previousUi = uIs.get(previousUiId);
+ assert previousUi != null
+ && embedId.equals(previousUi.getEmbedId()) : "UI id map and embed id map not in sync";
+
+ // Will fire cleanup events at the end of the request handling.
+ previousUi.close();
+ }
+ }
}
/**
@@ -1340,4 +1328,25 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
stream.defaultReadObject();
pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
}
+
+ /**
+ * Finds the UI with the corresponding embed id.
+ *
+ * @since 7.2
+ * @param embedId
+ * the embed id
+ * @return the UI with the corresponding embed id, or <code>null</code> if
+ * no UI is found
+ *
+ * @see UI#getEmbedId()
+ */
+ public UI getUIByEmbedId(String embedId) {
+ Integer uiId = embedIdMap.get(embedId);
+ if (uiId == null) {
+ return null;
+ } else {
+ return getUIById(uiId.intValue());
+ }
+ }
+
}
diff --git a/server/src/com/vaadin/server/communication/FileUploadHandler.java b/server/src/com/vaadin/server/communication/FileUploadHandler.java
index 3f6bfd9267..41a16601fe 100644
--- a/server/src/com/vaadin/server/communication/FileUploadHandler.java
+++ b/server/src/com/vaadin/server/communication/FileUploadHandler.java
@@ -284,7 +284,7 @@ public class FileUploadHandler implements RequestHandler {
// if boundary string does not exist, the posted file is from
// XHR2.post(File)
doHandleXhrFilePost(session, request, response, streamVariable,
- variableName, source, request.getContentLength());
+ variableName, source, getContentLength(request));
}
return true;
}
@@ -336,7 +336,7 @@ public class FileUploadHandler implements RequestHandler {
final InputStream inputStream = request.getInputStream();
- int contentLength = request.getContentLength();
+ long contentLength = getContentLength(request);
boolean atStart = false;
boolean firstFileFieldFound = false;
@@ -403,9 +403,22 @@ public class FileUploadHandler implements RequestHandler {
}
+ /*
+ * request.getContentLength() is limited to "int" by the Servlet
+ * specification. To support larger file uploads manually evaluate the
+ * Content-Length header which can contain long values.
+ */
+ private long getContentLength(VaadinRequest request) {
+ try {
+ return Long.parseLong(request.getHeader("Content-Length"));
+ } catch (NumberFormatException e) {
+ return -1l;
+ }
+ }
+
private void handleFileUploadValidationAndData(VaadinSession session,
InputStream inputStream, StreamVariable streamVariable,
- String filename, String mimeType, int contentLength,
+ String filename, String mimeType, long contentLength,
ClientConnector connector, String variableName)
throws UploadException {
session.lock();
@@ -474,7 +487,7 @@ public class FileUploadHandler implements RequestHandler {
protected void doHandleXhrFilePost(VaadinSession session,
VaadinRequest request, VaadinResponse response,
StreamVariable streamVariable, String variableName,
- ClientConnector owner, int contentLength) throws IOException {
+ ClientConnector owner, long contentLength) throws IOException {
// These are unknown in filexhr ATM, maybe add to Accept header that
// is accessible in portlets
@@ -504,7 +517,7 @@ public class FileUploadHandler implements RequestHandler {
*/
protected final boolean streamToReceiver(VaadinSession session,
final InputStream in, StreamVariable streamVariable,
- String filename, String type, int contentLength)
+ String filename, String type, long contentLength)
throws UploadException {
if (streamVariable == null) {
throw new IllegalStateException(
@@ -512,7 +525,7 @@ public class FileUploadHandler implements RequestHandler {
}
OutputStream out = null;
- int totalBytes = 0;
+ long totalBytes = 0;
StreamingStartEventImpl startedEvent = new StreamingStartEventImpl(
filename, type, contentLength);
try {
diff --git a/server/src/com/vaadin/server/communication/HeartbeatHandler.java b/server/src/com/vaadin/server/communication/HeartbeatHandler.java
index 4c95859203..04cb1b5a25 100644
--- a/server/src/com/vaadin/server/communication/HeartbeatHandler.java
+++ b/server/src/com/vaadin/server/communication/HeartbeatHandler.java
@@ -43,6 +43,11 @@ import com.vaadin.ui.UI;
public class HeartbeatHandler extends SynchronizedRequestHandler implements
SessionExpiredHandler {
+ @Override
+ protected boolean canHandleRequest(VaadinRequest request) {
+ return ServletPortletHelper.isHeartbeatRequest(request);
+ }
+
/**
* Handles a heartbeat request for the given session. Reads the GET
* parameter named {@link UIConstants#UI_ID_PARAMETER} to identify the UI.
@@ -53,10 +58,6 @@ public class HeartbeatHandler extends SynchronizedRequestHandler implements
@Override
public boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response) throws IOException {
- if (!ServletPortletHelper.isHeartbeatRequest(request)) {
- return false;
- }
-
UI ui = session.getService().findUI(request);
if (ui != null) {
ui.setLastHeartbeatTimestamp(System.currentTimeMillis());
diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java
index 8d0da24896..74595322a0 100644
--- a/server/src/com/vaadin/server/communication/PushRequestHandler.java
+++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java
@@ -28,6 +28,8 @@ import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResponse;
import com.vaadin.server.RequestHandler;
+import com.vaadin.server.ServiceDestroyEvent;
+import com.vaadin.server.ServiceDestroyListener;
import com.vaadin.server.ServiceException;
import com.vaadin.server.ServletPortletHelper;
import com.vaadin.server.SessionExpiredHandler;
@@ -63,6 +65,13 @@ public class PushRequestHandler implements RequestHandler,
}
};
+ service.addServiceDestroyListener(new ServiceDestroyListener() {
+ @Override
+ public void serviceDestroy(ServiceDestroyEvent event) {
+ destroy();
+ }
+ });
+
pushHandler = new PushHandler(service);
atmosphere.addAtmosphereHandler("/*", pushHandler);
atmosphere.addInitParameter(ApplicationConfig.PROPERTY_SESSION_SUPPORT,
diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java
index 3cc85909ee..eff9ceebf4 100644
--- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java
+++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java
@@ -20,8 +20,6 @@ import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.lang.reflect.Type;
-import java.text.CharacterIterator;
-import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -32,6 +30,7 @@ import java.util.logging.Logger;
import org.json.JSONArray;
import org.json.JSONException;
+import org.json.JSONObject;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.JsonCodec;
@@ -62,10 +61,71 @@ import com.vaadin.ui.UI;
*/
public class ServerRpcHandler implements Serializable {
- /* Variable records indexes */
- public static final char VAR_BURST_SEPARATOR = '\u001d';
+ /**
+ * A data transfer object representing an RPC request sent by the client
+ * side.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+ public static class RpcRequest {
+
+ private final String csrfToken;
+ private final JSONArray invocations;
+ private final int syncId;
+ private final JSONObject json;
+
+ public RpcRequest(String jsonString) throws JSONException {
+ json = new JSONObject(jsonString);
+ csrfToken = json.getString(ApplicationConstants.CSRF_TOKEN);
+ syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID);
+ invocations = new JSONArray(
+ json.getString(ApplicationConstants.RPC_INVOCATIONS));
+ }
- public static final char VAR_ESCAPE_CHARACTER = '\u001b';
+ /**
+ * Gets the CSRF security token (double submit cookie) for this request.
+ *
+ * @return the CSRF security token for this current change request
+ */
+ public String getCsrfToken() {
+ return csrfToken;
+ }
+
+ /**
+ * Gets the data to recreate the RPC as requested by the client side.
+ *
+ * @return the data describing which RPC should be made, and all their
+ * data
+ */
+ public JSONArray getRpcInvocationsData() {
+ return invocations;
+ }
+
+ /**
+ * Gets the sync id last seen by the client.
+ *
+ * @return the last sync id given by the server, according to the
+ * client's request
+ */
+ public int getSyncId() {
+ return syncId;
+ }
+
+ /**
+ * Gets the entire request in JSON format, as it was received from the
+ * client.
+ * <p>
+ * <em>Note:</em> This is a shared reference - any modifications made
+ * will be shared.
+ *
+ * @return the raw JSON object that was received from the client
+ *
+ */
+ public JSONObject getRawJson() {
+ return json;
+ }
+ }
private static final int MAX_BUFFER_SIZE = 64 * 1024;
@@ -90,45 +150,50 @@ public class ServerRpcHandler implements Serializable {
throws IOException, InvalidUIDLSecurityKeyException, JSONException {
ui.getSession().setLastRequestTimestamp(System.currentTimeMillis());
- String changes = getMessage(reader);
+ String changeMessage = getMessage(reader);
- final String[] bursts = changes.split(String
- .valueOf(VAR_BURST_SEPARATOR));
-
- if (bursts.length > 2) {
- throw new RuntimeException(
- "Multiple variable bursts not supported in Vaadin 7");
- } else if (bursts.length <= 1) {
+ if (changeMessage == null || changeMessage.equals("")) {
// The client sometimes sends empty messages, this is probably a bug
return;
}
+ RpcRequest rpcRequest = new RpcRequest(changeMessage);
+
// Security: double cookie submission pattern unless disabled by
// property
- if (!VaadinService.isCsrfTokenValid(ui.getSession(), bursts[0])) {
+ if (!VaadinService.isCsrfTokenValid(ui.getSession(),
+ rpcRequest.getCsrfToken())) {
throw new InvalidUIDLSecurityKeyException("");
}
- handleBurst(ui, unescapeBurst(bursts[1]));
+ handleInvocations(ui, rpcRequest.getSyncId(),
+ rpcRequest.getRpcInvocationsData());
+
+ ui.getConnectorTracker().cleanConcurrentlyRemovedConnectorIds(
+ rpcRequest.getSyncId());
}
/**
- * Processes a message burst received from the client.
- *
- * A burst can contain any number of RPC calls, including legacy variable
- * change calls that are processed separately.
- *
+ * Processes invocations data received from the client.
+ * <p>
+ * The invocations data can contain any number of RPC calls, including
+ * legacy variable change calls that are processed separately.
+ * <p>
* Consecutive changes to the value of the same variable are combined and
* changeVariables() is only called once for them. This preserves the Vaadin
* 6 semantics for components and add-ons that do not use Vaadin 7 RPC
* directly.
*
- * @param source
* @param uI
- * the UI receiving the burst
- * @param burst
- * the content of the burst as a String to be parsed
+ * the UI receiving the invocations data
+ * @param lastSyncIdSeenByClient
+ * the most recent sync id the client has seen at the time the
+ * request was sent
+ * @param invocationsData
+ * JSON containing all information needed to execute all
+ * requested RPC calls.
*/
- private void handleBurst(UI uI, String burst) {
+ private void handleInvocations(UI uI, int lastSyncIdSeenByClient,
+ JSONArray invocationsData) {
// TODO PUSH Refactor so that this is not needed
LegacyCommunicationManager manager = uI.getSession()
.getCommunicationManager();
@@ -137,7 +202,8 @@ public class ServerRpcHandler implements Serializable {
Set<Connector> enabledConnectors = new HashSet<Connector>();
List<MethodInvocation> invocations = parseInvocations(
- uI.getConnectorTracker(), burst);
+ uI.getConnectorTracker(), invocationsData,
+ lastSyncIdSeenByClient);
for (MethodInvocation invocation : invocations) {
final ClientConnector connector = manager.getConnector(uI,
invocation.getConnectorId());
@@ -250,21 +316,22 @@ public class ServerRpcHandler implements Serializable {
}
/**
- * Parse a message burst from the client into a list of MethodInvocation
- * instances.
+ * Parse JSON from the client into a list of MethodInvocation instances.
*
* @param connectorTracker
* The ConnectorTracker used to lookup connectors
- * @param burst
- * message string (JSON)
+ * @param invocationsJson
+ * JSON containing all information needed to execute all
+ * requested RPC calls.
+ * @param lastSyncIdSeenByClient
+ * the most recent sync id the client has seen at the time the
+ * request was sent
* @return list of MethodInvocation to perform
* @throws JSONException
*/
private List<MethodInvocation> parseInvocations(
- ConnectorTracker connectorTracker, String burst)
- throws JSONException {
- JSONArray invocationsJson = new JSONArray(burst);
-
+ ConnectorTracker connectorTracker, JSONArray invocationsJson,
+ int lastSyncIdSeenByClient) throws JSONException {
ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>();
MethodInvocation previousInvocation = null;
@@ -274,7 +341,8 @@ public class ServerRpcHandler implements Serializable {
JSONArray invocationJson = invocationsJson.getJSONArray(i);
MethodInvocation invocation = parseInvocation(invocationJson,
- previousInvocation, connectorTracker);
+ previousInvocation, connectorTracker,
+ lastSyncIdSeenByClient);
if (invocation != null) {
// Can be null if the invocation was a legacy invocation and it
// was merged with the previous one or if the invocation was
@@ -288,7 +356,8 @@ public class ServerRpcHandler implements Serializable {
private MethodInvocation parseInvocation(JSONArray invocationJson,
MethodInvocation previousInvocation,
- ConnectorTracker connectorTracker) throws JSONException {
+ ConnectorTracker connectorTracker, long lastSyncIdSeenByClient)
+ throws JSONException {
String connectorId = invocationJson.getString(0);
String interfaceName = invocationJson.getString(1);
String methodName = invocationJson.getString(2);
@@ -296,18 +365,22 @@ public class ServerRpcHandler implements Serializable {
if (connectorTracker.getConnector(connectorId) == null
&& !connectorId
.equals(ApplicationConstants.DRAG_AND_DROP_CONNECTOR_ID)) {
- getLogger()
- .log(Level.WARNING,
- "RPC call to "
- + interfaceName
- + "."
- + methodName
- + " received for connector "
- + connectorId
- + " but no such connector could be found. Resynchronizing client.");
- // This is likely an out of sync issue (client tries to update a
- // connector which is not present). Force resync.
- connectorTracker.markAllConnectorsDirty();
+
+ if (!connectorTracker.connectorWasPresentAsRequestWasSent(
+ connectorId, lastSyncIdSeenByClient)) {
+ getLogger()
+ .log(Level.WARNING,
+ "RPC call to "
+ + interfaceName
+ + "."
+ + methodName
+ + " received for connector "
+ + connectorId
+ + " but no such connector could be found. Resynchronizing client.");
+ // This is likely an out of sync issue (client tries to update a
+ // connector which is not present). Force resync.
+ connectorTracker.markAllConnectorsDirty();
+ }
return null;
}
@@ -403,50 +476,6 @@ public class ServerRpcHandler implements Serializable {
owner.changeVariables(source, m);
}
- /**
- * Unescape encoded burst separator characters in a burst received from the
- * client. This protects from separator injection attacks.
- *
- * @param encodedValue
- * to decode
- * @return decoded value
- */
- protected String unescapeBurst(String encodedValue) {
- final StringBuilder result = new StringBuilder();
- final StringCharacterIterator iterator = new StringCharacterIterator(
- encodedValue);
- char character = iterator.current();
- while (character != CharacterIterator.DONE) {
- if (VAR_ESCAPE_CHARACTER == character) {
- character = iterator.next();
- switch (character) {
- case VAR_ESCAPE_CHARACTER + 0x30:
- // escaped escape character
- result.append(VAR_ESCAPE_CHARACTER);
- break;
- case VAR_BURST_SEPARATOR + 0x30:
- // +0x30 makes these letters for easier reading
- result.append((char) (character - 0x30));
- break;
- case CharacterIterator.DONE:
- // error
- throw new RuntimeException(
- "Communication error: Unexpected end of message");
- default:
- // other escaped character - probably a client-server
- // version mismatch
- throw new RuntimeException(
- "Invalid escaped character from the client - check that the widgetset and server versions match");
- }
- } else {
- // not a special character - add it to the result as is
- result.append(character);
- }
- character = iterator.next();
- }
- return result.toString();
- }
-
protected String getMessage(Reader reader) throws IOException {
StringBuilder sb = new StringBuilder(MAX_BUFFER_SIZE);
diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java
index d4b0bc709f..6ab9d9dc58 100644
--- a/server/src/com/vaadin/server/communication/UIInitHandler.java
+++ b/server/src/com/vaadin/server/communication/UIInitHandler.java
@@ -20,7 +20,6 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.util.List;
-import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -38,6 +37,7 @@ import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.JsonConstants;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.shared.ui.ui.UIConstants;
@@ -56,12 +56,13 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
protected abstract boolean isInitRequest(VaadinRequest request);
@Override
+ protected boolean canHandleRequest(VaadinRequest request) {
+ return isInitRequest(request);
+ }
+
+ @Override
public boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response) throws IOException {
- if (!isInitRequest(request)) {
- return false;
- }
-
StringWriter stringWriter = new StringWriter();
try {
@@ -107,7 +108,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
static boolean commitJsonResponse(VaadinRequest request,
VaadinResponse response, String json) throws IOException {
// The response was produced without errors so write it to the client
- response.setContentType("application/json; charset=UTF-8");
+ response.setContentType(JsonConstants.JSON_CONTENT_TYPE);
// Ensure that the browser does not cache UIDL responses.
// iOS 6 Safari requires this (#9732)
@@ -163,31 +164,29 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
return null;
}
- // Check for an existing UI based on window.name
+ // Check for an existing UI based on embed id
- // Special parameter sent by vaadinBootstrap.js
- String windowName = request.getParameter("v-wn");
+ String embedId = getEmbedId(request);
- Map<String, Integer> retainOnRefreshUIs = session
- .getPreserveOnRefreshUIs();
- if (windowName != null && !retainOnRefreshUIs.isEmpty()) {
- // Check for a known UI
-
- Integer retainedUIId = retainOnRefreshUIs.get(windowName);
-
- if (retainedUIId != null) {
- UI retainedUI = session.getUIById(retainedUIId.intValue());
+ UI retainedUI = session.getUIByEmbedId(embedId);
+ if (retainedUI != null) {
+ if (vaadinService.preserveUIOnRefresh(provider, new UICreateEvent(
+ request, uiClass))) {
if (uiClass.isInstance(retainedUI)) {
reinitUI(retainedUI, request);
return retainedUI;
} else {
getLogger().info(
- "Not using retained UI in " + windowName
- + " because retained UI was of type "
+ "Not using the preserved UI " + embedId
+ + " because it is of type "
+ retainedUI.getClass() + " but " + uiClass
+ " is expected for the request.");
}
}
+ /*
+ * Previous UI without preserve on refresh will be closed when the
+ * new UI gets added to the session.
+ */
}
// No existing UI found - go on by creating and initializing one
@@ -220,26 +219,45 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
// Set thread local here so it is available in init
UI.setCurrent(ui);
- ui.doInit(request, uiId.intValue());
+ ui.doInit(request, uiId.intValue(), embedId);
session.addUI(ui);
- // Remember if it should be remembered
- if (vaadinService.preserveUIOnRefresh(provider, event)) {
- // Remember this UI
- if (windowName == null) {
- getLogger().warning(
- "There is no window.name available for UI " + uiClass
- + " that should be preserved.");
- } else {
- session.getPreserveOnRefreshUIs().put(windowName, uiId);
- }
+ // Warn if the window can't be preserved
+ if (embedId == null
+ && vaadinService.preserveUIOnRefresh(provider, event)) {
+ getLogger().warning(
+ "There is no embed id available for UI " + uiClass
+ + " that should be preserved.");
}
return ui;
}
/**
+ * Constructs an embed id based on information in the request.
+ *
+ * @since 7.2
+ *
+ * @param request
+ * the request to get embed information from
+ * @return the embed id, or <code>null</code> if id is not available.
+ *
+ * @see UI#getEmbedId()
+ */
+ protected String getEmbedId(VaadinRequest request) {
+ // Parameters sent by vaadinBootstrap.js
+ String windowName = request.getParameter("v-wn");
+ String appId = request.getParameter("v-appId");
+
+ if (windowName != null && appId != null) {
+ return windowName + '.' + appId;
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Updates a UI that has already been initialized but is now loaded again,
* e.g. because of {@link PreserveOnRefresh}.
*
diff --git a/server/src/com/vaadin/server/communication/UidlRequestHandler.java b/server/src/com/vaadin/server/communication/UidlRequestHandler.java
index f13199e9ae..2277e1c76f 100644
--- a/server/src/com/vaadin/server/communication/UidlRequestHandler.java
+++ b/server/src/com/vaadin/server/communication/UidlRequestHandler.java
@@ -59,11 +59,13 @@ public class UidlRequestHandler extends SynchronizedRequestHandler implements
private UidlWriter uidlWriter = new UidlWriter();
@Override
+ protected boolean canHandleRequest(VaadinRequest request) {
+ return ServletPortletHelper.isUIDLRequest(request);
+ }
+
+ @Override
public boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response) throws IOException {
- if (!ServletPortletHelper.isUIDLRequest(request)) {
- return false;
- }
UI uI = session.getService().findUI(request);
if (uI == null) {
// This should not happen but it will if the UI has been closed. We
diff --git a/server/src/com/vaadin/server/communication/UidlWriter.java b/server/src/com/vaadin/server/communication/UidlWriter.java
index fe7a7d42bf..a290eb4846 100644
--- a/server/src/com/vaadin/server/communication/UidlWriter.java
+++ b/server/src/com/vaadin/server/communication/UidlWriter.java
@@ -38,6 +38,7 @@ import com.vaadin.server.LegacyCommunicationManager;
import com.vaadin.server.LegacyCommunicationManager.ClientCache;
import com.vaadin.server.SystemMessages;
import com.vaadin.server.VaadinSession;
+import com.vaadin.shared.ApplicationConstants;
import com.vaadin.ui.ConnectorTracker;
import com.vaadin.ui.UI;
@@ -105,6 +106,9 @@ public class UidlWriter implements Serializable {
uiConnectorTracker.setWritingResponse(true);
try {
+ writer.write("\"" + ApplicationConstants.SERVER_SYNC_ID
+ + "\": " + uiConnectorTracker.getCurrentSyncId() + ", ");
+
writer.write("\"changes\" : ");
LegacyCommunicationManager manager = session
diff --git a/server/src/com/vaadin/ui/Component.java b/server/src/com/vaadin/ui/Component.java
index 485327bb54..c385805675 100644
--- a/server/src/com/vaadin/ui/Component.java
+++ b/server/src/com/vaadin/ui/Component.java
@@ -651,7 +651,7 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
public Locale getLocale();
/**
- * Adds an unique id for component that get's transferred to terminal for
+ * Adds an unique id for component that is used in the client-side for
* testing purposes. Keeping identifiers unique is the responsibility of the
* programmer.
*
@@ -661,7 +661,7 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
public void setId(String id);
/**
- * Get's currently set debug identifier
+ * Gets currently set debug identifier
*
* @return current id, null if not set
*/
@@ -669,7 +669,7 @@ public interface Component extends ClientConnector, Sizeable, Serializable {
/**
* <p>
- * Gets the component's description, used in tooltips and can be displayed
+ * Gets the components description, used in tooltips and can be displayed
* directly in certain other components such as forms. The description can
* be used to briefly describe the state of the component to the user. The
* description string may contain certain XML tags:
diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java
index 0f8ec60104..33d585adca 100644
--- a/server/src/com/vaadin/ui/ConnectorTracker.java
+++ b/server/src/com/vaadin/ui/ConnectorTracker.java
@@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -81,6 +82,16 @@ public class ConnectorTracker implements Serializable {
private Map<StreamVariable, String> streamVariableToSeckey;
+ private int currentSyncId = 0;
+
+ /**
+ * Map to track on which syncId each connector was removed.
+ *
+ * @see #getCurrentSyncId()
+ * @see #cleanConcurrentlyRemovedConnectorIds(long)
+ */
+ private TreeMap<Integer, Set<String>> syncIdToUnregisteredConnectorIds = new TreeMap<Integer, Set<String>>();
+
/**
* Gets a logger for this class
*
@@ -170,6 +181,15 @@ public class ConnectorTracker implements Serializable {
+ " is not the one that was registered for that id");
}
+ Set<String> unregisteredConnectorIds = syncIdToUnregisteredConnectorIds
+ .get(currentSyncId);
+ if (unregisteredConnectorIds == null) {
+ unregisteredConnectorIds = new HashSet<String>();
+ syncIdToUnregisteredConnectorIds.put(currentSyncId,
+ unregisteredConnectorIds);
+ }
+ unregisteredConnectorIds.add(connectorId);
+
dirtyConnectors.remove(connector);
if (unregisteredConnectors.add(connector)) {
if (getLogger().isLoggable(Level.FINE)) {
@@ -570,12 +590,18 @@ public class ConnectorTracker implements Serializable {
/**
* Sets the current response write status. Connectors can not be marked as
* dirty when the response is written.
+ * <p>
+ * This method has a side-effect of incrementing the sync id by one (see
+ * {@link #getCurrentSyncId()}), if {@link #isWritingResponse()} returns
+ * <code>false</code> and <code>writingResponse</code> is set to
+ * <code>true</code>.
*
* @param writingResponse
* the new response status.
*
* @see #markDirty(ClientConnector)
* @see #isWritingResponse()
+ * @see #getCurrentSyncId()
*
* @throws IllegalArgumentException
* if the new response status is the same as the previous value.
@@ -587,6 +613,14 @@ public class ConnectorTracker implements Serializable {
throw new IllegalArgumentException(
"The old value is same as the new value");
}
+
+ /*
+ * the right hand side of the && is unnecessary here because of the
+ * if-clause above, but rigorous coding is always rigorous coding.
+ */
+ if (writingResponse && !this.writingResponse) {
+ currentSyncId++;
+ }
this.writingResponse = writingResponse;
}
@@ -732,4 +766,105 @@ public class ConnectorTracker implements Serializable {
}
return streamVariableToSeckey.get(variable);
}
+
+ /**
+ * Check whether a connector was present on the client when the it was
+ * creating this request, but was removed server-side before the request
+ * arrived.
+ *
+ * @since 7.2
+ * @param connectorId
+ * The connector id to check for whether it was removed
+ * concurrently or not.
+ * @param lastSyncIdSeenByClient
+ * the most recent sync id the client has seen at the time the
+ * request was sent
+ * @return <code>true</code> if the connector was removed before the client
+ * had a chance to react to it.
+ */
+ public boolean connectorWasPresentAsRequestWasSent(String connectorId,
+ long lastSyncIdSeenByClient) {
+
+ assert getConnector(connectorId) == null : "Connector " + connectorId
+ + " is still attached";
+
+ boolean clientRequestIsTooOld = lastSyncIdSeenByClient < currentSyncId;
+ if (clientRequestIsTooOld) {
+ /*
+ * The headMap call is present here because we're only interested in
+ * connectors removed "in the past" (i.e. the server has removed
+ * them before the client ever knew about that), since those are the
+ * ones that we choose to handle as a special case.
+ */
+ /*-
+ * Server Client
+ * [#1 add table] ---------.
+ * \
+ * [push: #2 remove table]-. `--> [adding table, storing #1]
+ * \ .- [table from request #1 needs more data]
+ * \/
+ * /`-> [removing table, storing #2]
+ * [#1 < #2 - ignoring] <---´
+ */
+ for (Set<String> unregisteredConnectors : syncIdToUnregisteredConnectorIds
+ .headMap(currentSyncId).values()) {
+ if (unregisteredConnectors.contains(connectorId)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets the most recently generated server sync id.
+ * <p>
+ * The sync id is incremented by one whenever a new response is being
+ * written. This id is then sent over to the client. The client then adds
+ * the most recent sync id to each communication packet it sends back to the
+ * server. This way, the server knows at what state the client is when the
+ * packet is sent. If the state has changed on the server side since that,
+ * the server can try to adjust the way it handles the actions from the
+ * client side.
+ *
+ * @see #setWritingResponse(boolean)
+ * @see #connectorWasPresentAsRequestWasSent(String, long)
+ * @since 7.2
+ * @return the current sync id
+ */
+ public int getCurrentSyncId() {
+ return currentSyncId;
+ }
+
+ /**
+ * Maintains the bookkeeping connector removal and concurrency by removing
+ * entries that have become too old.
+ * <p>
+ * <em>It is important to run this call for each transmission from the client</em>
+ * , otherwise the bookkeeping gets out of date and the results form
+ * {@link #connectorWasPresentAsRequestWasSent(String, long)} will become
+ * invalid (that is, even though the client knew the component was removed,
+ * the aforementioned method would start claiming otherwise).
+ * <p>
+ * Entries that both client and server agree upon are removed. Since
+ * argument is the last sync id that the client has seen from the server, we
+ * know that entries earlier than that cannot cause any problems anymore.
+ *
+ * @see #connectorWasPresentAsRequestWasSent(String, long)
+ * @since 7.2
+ * @param lastSyncIdSeenByClient
+ * the sync id the client has most recently received from the
+ * server.
+ */
+ public void cleanConcurrentlyRemovedConnectorIds(int lastSyncIdSeenByClient) {
+ /*
+ * We remove all entries _older_ than the one reported right now,
+ * because the remaining still contain components that might cause
+ * conflicts. In any case, it's better to clean up too little than too
+ * much, especially as the data will hardly grow into the kilobytes.
+ */
+ syncIdToUnregisteredConnectorIds.headMap(lastSyncIdSeenByClient)
+ .clear();
+ }
}
diff --git a/server/src/com/vaadin/ui/DragAndDropWrapper.java b/server/src/com/vaadin/ui/DragAndDropWrapper.java
index 5d6825c868..2ab3e872c6 100644
--- a/server/src/com/vaadin/ui/DragAndDropWrapper.java
+++ b/server/src/com/vaadin/ui/DragAndDropWrapper.java
@@ -56,7 +56,7 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
for (int i = 0; i < fc; i++) {
Html5File file = new Html5File(
(String) rawVariables.get("fn" + i), // name
- (Integer) rawVariables.get("fs" + i), // size
+ ((Double) rawVariables.get("fs" + i)).longValue(), // size
(String) rawVariables.get("ft" + i)); // mime
String id = (String) rawVariables.get("fi" + i);
files[i] = file;
diff --git a/server/src/com/vaadin/ui/Link.java b/server/src/com/vaadin/ui/Link.java
index cf8e1a9693..e1a47777bd 100644
--- a/server/src/com/vaadin/ui/Link.java
+++ b/server/src/com/vaadin/ui/Link.java
@@ -16,13 +16,10 @@
package com.vaadin.ui;
-import java.util.Map;
-
-import com.vaadin.server.PaintException;
-import com.vaadin.server.PaintTarget;
import com.vaadin.server.Resource;
import com.vaadin.shared.ui.BorderStyle;
import com.vaadin.shared.ui.link.LinkConstants;
+import com.vaadin.shared.ui.link.LinkState;
/**
* Link is used to create external or internal URL links.
@@ -31,7 +28,7 @@ import com.vaadin.shared.ui.link.LinkConstants;
* @since 3.0
*/
@SuppressWarnings("serial")
-public class Link extends AbstractComponent implements LegacyComponent {
+public class Link extends AbstractComponent {
/**
* @deprecated As of 7.0, use {@link BorderStyle#NONE} instead
@@ -51,14 +48,6 @@ public class Link extends AbstractComponent implements LegacyComponent {
@Deprecated
public static final BorderStyle TARGET_BORDER_DEFAULT = BorderStyle.DEFAULT;
- private String targetName;
-
- private BorderStyle targetBorder = BorderStyle.DEFAULT;
-
- private int targetWidth = -1;
-
- private int targetHeight = -1;
-
/**
* Creates a new link.
*/
@@ -105,43 +94,14 @@ public class Link extends AbstractComponent implements LegacyComponent {
setTargetBorder(border);
}
- /**
- * Paints the content of this component.
- *
- * @param target
- * the Paint Event.
- * @throws PaintException
- * if the paint operation failed.
- */
@Override
- public void paintContent(PaintTarget target) throws PaintException {
- if (getResource() == null) {
- return;
- }
-
- // Target window name
- final String name = getTargetName();
- if (name != null && name.length() > 0) {
- target.addAttribute("name", name);
- }
-
- // Target window size
- if (getTargetWidth() >= 0) {
- target.addAttribute("targetWidth", getTargetWidth());
- }
- if (getTargetHeight() >= 0) {
- target.addAttribute("targetHeight", getTargetHeight());
- }
-
- // Target window border
- switch (getTargetBorder()) {
- case MINIMAL:
- target.addAttribute("border", "minimal");
- break;
- case NONE:
- target.addAttribute("border", "none");
- break;
- }
+ protected LinkState getState() {
+ return (LinkState) super.getState();
+ }
+
+ @Override
+ protected LinkState getState(boolean markAsDirty) {
+ return (LinkState) super.getState(markAsDirty);
}
/**
@@ -150,7 +110,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window border.
*/
public BorderStyle getTargetBorder() {
- return targetBorder;
+ return getState(false).targetBorder;
}
/**
@@ -159,7 +119,8 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window height.
*/
public int getTargetHeight() {
- return targetHeight < 0 ? -1 : targetHeight;
+ return getState(false).targetHeight < 0 ? -1
+ : getState(false).targetHeight;
}
/**
@@ -169,7 +130,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window name.
*/
public String getTargetName() {
- return targetName;
+ return getState(false).target;
}
/**
@@ -178,7 +139,8 @@ public class Link extends AbstractComponent implements LegacyComponent {
* @return the target window width.
*/
public int getTargetWidth() {
- return targetWidth < 0 ? -1 : targetWidth;
+ return getState(false).targetWidth < 0 ? -1
+ : getState(false).targetWidth;
}
/**
@@ -188,8 +150,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetBorder to set.
*/
public void setTargetBorder(BorderStyle targetBorder) {
- this.targetBorder = targetBorder;
- markAsDirty();
+ getState().targetBorder = targetBorder;
}
/**
@@ -199,8 +160,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetHeight to set.
*/
public void setTargetHeight(int targetHeight) {
- this.targetHeight = targetHeight;
- markAsDirty();
+ getState().targetHeight = targetHeight;
}
/**
@@ -210,8 +170,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetName to set.
*/
public void setTargetName(String targetName) {
- this.targetName = targetName;
- markAsDirty();
+ getState().target = targetName;
}
/**
@@ -221,8 +180,7 @@ public class Link extends AbstractComponent implements LegacyComponent {
* the targetWidth to set.
*/
public void setTargetWidth(int targetWidth) {
- this.targetWidth = targetWidth;
- markAsDirty();
+ getState().targetWidth = targetWidth;
}
/**
@@ -244,8 +202,4 @@ public class Link extends AbstractComponent implements LegacyComponent {
setResource(LinkConstants.HREF_RESOURCE, resource);
}
- @Override
- public void changeVariables(Object source, Map<String, Object> variables) {
- // TODO Remove once LegacyComponent is no longer implemented
- }
}
diff --git a/server/src/com/vaadin/ui/Notification.java b/server/src/com/vaadin/ui/Notification.java
index cf1d03ab5c..31fa265b02 100644
--- a/server/src/com/vaadin/ui/Notification.java
+++ b/server/src/com/vaadin/ui/Notification.java
@@ -21,6 +21,7 @@ import java.io.Serializable;
import com.vaadin.server.Page;
import com.vaadin.server.Resource;
import com.vaadin.shared.Position;
+import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role;
/**
* A notification message, used to display temporary messages to the user - for
@@ -63,7 +64,7 @@ import com.vaadin.shared.Position;
*/
public class Notification implements Serializable {
public enum Type {
- HUMANIZED_MESSAGE, WARNING_MESSAGE, ERROR_MESSAGE, TRAY_NOTIFICATION;
+ HUMANIZED_MESSAGE, WARNING_MESSAGE, ERROR_MESSAGE, TRAY_NOTIFICATION, ASSISTIVE_NOTIFICATION;
}
@Deprecated
@@ -190,21 +191,38 @@ public class Notification implements Serializable {
case WARNING_MESSAGE:
delayMsec = 1500;
styleName = "warning";
+ setNavigationConfiguration("Warning: ", "", Role.ALERT);
break;
case ERROR_MESSAGE:
delayMsec = -1;
styleName = "error";
+ setNavigationConfiguration("Error: ", " - close with ESC",
+ Role.ALERT);
break;
case TRAY_NOTIFICATION:
delayMsec = 3000;
position = Position.BOTTOM_RIGHT;
styleName = "tray";
-
+ setNavigationConfiguration("Info: ", "", Role.STATUS);
+ break;
+ case ASSISTIVE_NOTIFICATION:
+ delayMsec = 3000;
+ position = Position.ASSISTIVE;
+ styleName = "assistive";
+ setNavigationConfiguration("Note: ", "", Role.ALERT);
+ break;
case HUMANIZED_MESSAGE:
default:
+ styleName = "humanized";
+ setNavigationConfiguration("Info: ", "", Role.ALERT);
break;
}
+ }
+ private void setNavigationConfiguration(String prefix, String postfix,
+ Role ariaRole) {
+ UI.getCurrent().getNotificationConfiguration()
+ .setStyleConfiguration(styleName, prefix, postfix, ariaRole);
}
/**
@@ -322,6 +340,132 @@ public class Notification implements Serializable {
}
/**
+ * Sets the accessibility prefix for a notification type.
+ *
+ * This prefix is read to assistive device users before the content of the
+ * notification, but not visible on the page.
+ *
+ * @param type
+ * Type of the notification
+ * @param prefix
+ * String that is placed before the notification content
+ */
+ public void setAssistivePrefixForType(Type type, String prefix) {
+ UI.getCurrent().getNotificationConfiguration()
+ .setAssistivePrefixForStyle(getStyle(type), prefix);
+ }
+
+ /**
+ * Gets the accessibility prefix for a notification type.
+ *
+ * This prefix is read to assistive device users before the content of the
+ * notification, but not visible on the page.
+ *
+ * @param type
+ * Type of the notification
+ * @return The accessibility prefix for the provided notification type
+ */
+ public String getAssistivePrefixForType(Type type) {
+ return UI.getCurrent().getNotificationConfiguration()
+ .getAssistivePrefixForStyle(getStyle(type));
+ }
+
+ /**
+ * Sets the accessibility postfix for a notification type.
+ *
+ * This postfix is read to assistive device users after the content of the
+ * notification, but not visible on the page.
+ *
+ * @param type
+ * Type of the notification
+ * @param postfix
+ * String that is placed after the notification content
+ */
+ public void setAssistivePostfixForType(Type type, String postfix) {
+ UI.getCurrent().getNotificationConfiguration()
+ .setAssistivePostfixForStyle(getStyle(type), postfix);
+ }
+
+ /**
+ * Gets the accessibility postfix for a notification type.
+ *
+ * This postfix is read to assistive device users after the content of the
+ * notification, but not visible on the page.
+ *
+ * @param type
+ * Type of the notification
+ * @return The accessibility postfix for the provided notification type
+ */
+ public String getAssistivePostfixForType(Type type) {
+ return UI.getCurrent().getNotificationConfiguration()
+ .getAssistivePostfixForStyle(getStyle(type));
+ }
+
+ /**
+ * Sets the WAI-ARIA role for a notification type.
+ *
+ * This role defines how an assistive device handles a notification.
+ * Available roles are alert and status (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * The default role is alert.
+ *
+ * @param type
+ * Type of the notification
+ * @param role
+ * Role to set for the notification type
+ */
+ public void setAssistiveRoleForType(Type type, Role role) {
+ UI.getCurrent().getNotificationConfiguration()
+ .setAssistiveRoleForStyle(getStyle(type), role);
+ }
+
+ /**
+ * Gets the WAI-ARIA role for a notification type.
+ *
+ * This role defines how an assistive device handles a notification.
+ * Available roles are alert and status (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>)
+ *
+ * The default role is alert.
+ *
+ * @param type
+ * Type of the notification
+ * @return Role to set for the notification type
+ */
+ public Role getAssistiveRoleForType(Type type) {
+ return UI.getCurrent().getNotificationConfiguration()
+ .getAssistiveRoleForStyle(getStyle(type));
+ }
+
+ private String getStyle(Type type) {
+ String style = "";
+
+ switch (type) {
+ case WARNING_MESSAGE:
+ style = "warning";
+ break;
+ case ERROR_MESSAGE:
+ style = "error";
+ break;
+ case TRAY_NOTIFICATION:
+ style = "tray";
+ break;
+ case ASSISTIVE_NOTIFICATION:
+ style = "assistive";
+ break;
+ case HUMANIZED_MESSAGE:
+ default:
+ style = "humanized";
+ break;
+ }
+
+ return style;
+ }
+
+ /**
* Sets whether html is allowed in the caption and description. If set to
* true, the texts are passed to the browser as html and the developer is
* responsible for ensuring no harmful html is used. If set to false, the
@@ -414,4 +558,4 @@ public class Notification implements Serializable {
public static void show(String caption, String description, Type type) {
new Notification(caption, description, type).show(Page.getCurrent());
}
-} \ No newline at end of file
+}
diff --git a/server/src/com/vaadin/ui/NotificationConfiguration.java b/server/src/com/vaadin/ui/NotificationConfiguration.java
new file mode 100644
index 0000000000..52d3e76d63
--- /dev/null
+++ b/server/src/com/vaadin/ui/NotificationConfiguration.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ *
+ */
+package com.vaadin.ui;
+
+import java.io.Serializable;
+
+import com.vaadin.shared.ui.ui.NotificationConfigurationBean;
+import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role;
+import com.vaadin.shared.ui.ui.UIState.NotificationConfigurationState;
+
+/**
+ * Provides methods for configuring the notification.
+ *
+ * @author Vaadin Ltd
+ * @since 7.1
+ */
+public interface NotificationConfiguration extends Serializable {
+ public void setStyleConfiguration(String style, String prefix,
+ String postfix, Role ariaRole);
+
+ /**
+ * Returns the complete configuration object for the given notification
+ * style.
+ *
+ * @param style
+ * String of the notification style to return
+ * @return The notification configuration object
+ */
+ public NotificationConfigurationBean getStyleConfiguration(String style);
+
+ /**
+ * Sets the accessibility prefix for the given notification style.
+ *
+ * This prefix is read to assistive device users in front of the content of
+ * the notification, but not visible on the page.
+ *
+ * @param style
+ * String of the notification style
+ * @param prefix
+ * String that is placed before the notification content
+ */
+ public void setAssistivePrefixForStyle(String style, String prefix);
+
+ /**
+ * Returns the accessibility prefix for the given notification style.
+ *
+ * This prefix is read to assistive device users in front of the content of
+ * the notification, but not visible on the page.
+ *
+ * @param style
+ * String of the notification style
+ * @return The prefix of the provided notification style
+ */
+ public String getAssistivePrefixForStyle(String style);
+
+ /**
+ * Sets the accessibility postfix for the given notification style.
+ *
+ * This postfix is read to assistive device users after the content of the
+ * notification, but not visible on the page.
+ *
+ * @param style
+ * String of the notification style
+ * @param postfix
+ * String that is placed after the notification content
+ */
+ public void setAssistivePostfixForStyle(String style, String postfix);
+
+ /**
+ * Returns the accessibility postfix for the given notification style.
+ *
+ * This postfix is read to assistive device users after the content of the
+ * notification, but not visible on the page.
+ *
+ * @param style
+ * String of the notification style
+ * @return The postfix of the provided notification style
+ */
+ public String getAssistivePostfixForStyle(String style);
+
+ /**
+ * Sets the WAI-ARIA role for a notification style.
+ *
+ * This role defines how an assistive device handles a notification.
+ * Available roles are alert, alertdialog and status (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>)
+ *
+ * The default role is alert.
+ *
+ * @param style
+ * String of the notification style
+ * @param role
+ * Role to set for the notification type
+ */
+ public void setAssistiveRoleForStyle(String style, Role role);
+
+ /**
+ * Returns the WAI-ARIA role for a notification style.
+ *
+ * This role defines how an assistive device handles a notification.
+ * Available roles are alert, alertdialog and status (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a> )
+ *
+ * The default role is alert.
+ *
+ * @param style
+ * String of the notification style
+ * @return The current Role for the notification type
+ */
+ public Role getAssistiveRoleForStyle(String style);
+}
+
+class NotificationConfigurationImpl implements NotificationConfiguration {
+
+ private UI ui;
+
+ public NotificationConfigurationImpl(UI ui) {
+ this.ui = ui;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.NotificationConfiguration#setStyleConfiguration(java.lang
+ * .String, java.lang.String, java.lang.String,
+ * com.vaadin.ui.NotificationConfiguration.Role)
+ */
+ @Override
+ public void setStyleConfiguration(String style, String prefix,
+ String postfix, Role ariaRole) {
+ getState().setup.put(style, new NotificationConfigurationBean(prefix,
+ postfix, ariaRole));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.NotificationConfiguration#getStyleConfiguration(java.lang
+ * .String)
+ */
+ @Override
+ public NotificationConfigurationBean getStyleConfiguration(String style) {
+ return getState(false).setup.get(style);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.NotificationConfiguration#setStylePrefix(java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public void setAssistivePrefixForStyle(String style, String prefix) {
+ getConfigurationBean(style).setAssistivePrefix(prefix);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.NotificationConfiguration#getStylePrefix(java.lang.String)
+ */
+ @Override
+ public String getAssistivePrefixForStyle(String style) {
+ NotificationConfigurationBean styleSetup = getState().setup.get(style);
+ if (styleSetup != null) {
+ return styleSetup.getAssistivePrefix();
+ }
+
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.NotificationConfiguration#setStylePostfix(com.vaadin.ui
+ * .Notification.Type, java.lang.String)
+ */
+ @Override
+ public void setAssistivePostfixForStyle(String style, String postfix) {
+ getConfigurationBean(style).setAssistivePostfix(postfix);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.NotificationConfiguration#getStylePostfix(com.vaadin.ui
+ * .Notification.Type)
+ */
+ @Override
+ public String getAssistivePostfixForStyle(String style) {
+ NotificationConfigurationBean styleSetup = getState().setup.get(style);
+ if (styleSetup != null) {
+ return styleSetup.getAssistivePostfix();
+ }
+
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.NotificationConfiguration#setStyleRole(com.vaadin.ui.
+ * Notification.Type, com.vaadin.ui.NotificationConfiguration.Role)
+ */
+ @Override
+ public void setAssistiveRoleForStyle(String style, Role role) {
+ getConfigurationBean(style).setAssistiveRole(role);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.NotificationConfiguration#getStyleRole(com.vaadin.ui.
+ * Notification.Type)
+ */
+ @Override
+ public Role getAssistiveRoleForStyle(String style) {
+ NotificationConfigurationBean styleSetup = getState().setup.get(style);
+ if (styleSetup != null) {
+ return styleSetup.getAssistiveRole();
+ }
+
+ return null;
+ }
+
+ private NotificationConfigurationBean getConfigurationBean(String style) {
+ NotificationConfigurationBean styleSetup = getState().setup.get(style);
+ if (styleSetup == null) {
+ styleSetup = new NotificationConfigurationBean();
+ getState().setup.put(style, styleSetup);
+ }
+
+ return styleSetup;
+ }
+
+ private NotificationConfigurationState getState() {
+ return ui.getState().notificationConfiguration;
+ }
+
+ private NotificationConfigurationState getState(boolean markAsDirty) {
+ return ui.getState(markAsDirty).notificationConfiguration;
+ }
+
+}
diff --git a/server/src/com/vaadin/ui/TabSheet.java b/server/src/com/vaadin/ui/TabSheet.java
index 36022adb74..a1f9e9dd26 100644
--- a/server/src/com/vaadin/ui/TabSheet.java
+++ b/server/src/com/vaadin/ui/TabSheet.java
@@ -268,7 +268,34 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @return the created {@link Tab}
*/
public Tab addTab(Component c, String caption, Resource icon) {
- return addTab(c, caption, icon, components.size());
+ return addTab(c, caption, icon, "", components.size());
+ }
+
+ /**
+ * Adds a new tab into TabSheet.
+ *
+ * The first tab added to a tab sheet is automatically selected and a tab
+ * selection event is fired.
+ *
+ * If the component is already present in the tab sheet, changes its caption
+ * and icon and icon alternate text and returns the corresponding (old) tab,
+ * preserving other tab metadata.
+ *
+ * @param c
+ * the component to be added onto tab - should not be null.
+ * @param caption
+ * the caption to be set for the component and used rendered in
+ * tab bar
+ * @param icon
+ * the icon to be set for the component and used rendered in tab
+ * bar
+ * @param iconAltText
+ * the alternate text for the icon
+ * @return the created {@link Tab}
+ */
+ public Tab addTab(Component c, String caption, Resource icon,
+ String iconAltText) {
+ return addTab(c, caption, icon, iconAltText, components.size());
}
/**
@@ -294,12 +321,41 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @return the created {@link Tab}
*/
public Tab addTab(Component c, String caption, Resource icon, int position) {
+ return addTab(c, caption, icon, "", position);
+ }
+
+ /**
+ * Adds a new tab into TabSheet.
+ *
+ * The first tab added to a tab sheet is automatically selected and a tab
+ * selection event is fired.
+ *
+ * If the component is already present in the tab sheet, changes its caption
+ * and icon and icon alternate text and returns the corresponding (old) tab,
+ * preserving other tab metadata like the position.
+ *
+ * @param c
+ * the component to be added onto tab - should not be null.
+ * @param caption
+ * the caption to be set for the component and used rendered in
+ * tab bar
+ * @param icon
+ * the icon to be set for the component and used rendered in tab
+ * bar
+ * @param iconAltText
+ * the alternate text for the icon
+ * @param position
+ * the position at where the the tab should be added.
+ * @return the created {@link Tab}
+ */
+ public Tab addTab(Component c, String caption, Resource icon,
+ String iconAltText, int position) {
if (c == null) {
return null;
} else if (tabs.containsKey(c)) {
Tab tab = tabs.get(c);
tab.setCaption(caption);
- tab.setIcon(icon);
+ tab.setIcon(icon, iconAltText);
return tab;
} else {
components.add(position, c);
@@ -371,13 +427,15 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
final Component c = i.next();
String caption = null;
Resource icon = null;
+ String iconAltText = "";
if (TabSheet.class.isAssignableFrom(source.getClass())) {
Tab tab = ((TabSheet) source).getTab(c);
caption = tab.getCaption();
icon = tab.getIcon();
+ iconAltText = tab.getIconAltText();
}
source.removeComponent(c);
- addTab(c, caption, icon);
+ addTab(c, caption, icon, iconAltText);
}
}
@@ -429,9 +487,12 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
if (icon != null) {
target.addAttribute(TabsheetBaseConstants.ATTRIBUTE_TAB_ICON,
icon);
+ target.addAttribute(
+ TabsheetBaseConstants.ATTRIBUTE_TAB_ICON_ALT,
+ tab.getIconAltText());
}
final String caption = tab.getCaption();
- if (caption != null && caption.length() > 0) {
+ if (caption != null && !caption.isEmpty()) {
target.addAttribute(
TabsheetBaseConstants.ATTRIBUTE_TAB_CAPTION, caption);
}
@@ -449,10 +510,15 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
}
final String styleName = tab.getStyleName();
- if (styleName != null && styleName.length() != 0) {
+ if (styleName != null && !styleName.isEmpty()) {
target.addAttribute(TabsheetConstants.TAB_STYLE_NAME, styleName);
}
+ final String id = tab.getId();
+ if (id != null && !id.isEmpty()) {
+ target.addAttribute("id", id);
+ }
+
target.addAttribute("key", keyMapper.key(component));
if (component.equals(selected)) {
target.addAttribute("selected", true);
@@ -549,6 +615,11 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
// connector
if (selected != null) {
selected.markAsDirtyRecursive();
+
+ Tab tab = getTab(c);
+ if (tab != null && tab.getDefaultFocusComponent() != null) {
+ tab.getDefaultFocusComponent().focus();
+ }
}
}
@@ -889,6 +960,23 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
public void setClosable(boolean closable);
/**
+ * Set the component that should automatically focused when the tab is
+ * selected.
+ *
+ * @param component
+ * the component to focus
+ */
+ public void setDefaultFocusComponent(Focusable component);
+
+ /**
+ * Get the component that should be automatically focused when the tab
+ * is selected.
+ *
+ * @return the focusable component
+ */
+ public Focusable getDefaultFocusComponent();
+
+ /**
* Returns the enabled status for the tab. A disabled tab is shown as
* such in the tab bar and cannot be selected.
*
@@ -932,6 +1020,27 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
public void setIcon(Resource icon);
/**
+ * Sets the icon and alt text for the tab.
+ *
+ * @param icon
+ * the icon to set
+ */
+ public void setIcon(Resource icon, String iconAltText);
+
+ /**
+ * Gets the icon alt text for the tab.
+ */
+ public String getIconAltText();
+
+ /**
+ * Sets the icon alt text for the tab.
+ *
+ * @param iconAltText
+ * the icon to set
+ */
+ public void setIconAltText(String iconAltText);
+
+ /**
* Gets the description for the tab. The description can be used to
* briefly describe the state of the tab to the user, and is typically
* shown as a tooltip when hovering over the tab.
@@ -1015,6 +1124,23 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
* @see #setStyleName(String)
*/
public String getStyleName();
+
+ /**
+ * Adds an unique id for component that is used in the client-side for
+ * testing purposes. Keeping identifiers unique is the responsibility of
+ * the programmer.
+ *
+ * @param id
+ * An alphanumeric id
+ */
+ public void setId(String id);
+
+ /**
+ * Gets currently set debug identifier
+ *
+ * @return current id, null if not set
+ */
+ public String getId();
}
/**
@@ -1030,6 +1156,9 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
private String description = null;
private ErrorMessage componentError = null;
private String styleName;
+ private String id;
+ private String iconAltText = "";
+ private Focusable defaultFocus;
public TabSheetTabImpl(String caption, Resource icon) {
if (caption == null) {
@@ -1061,11 +1190,38 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
@Override
public void setIcon(Resource icon) {
+ setIcon(icon, "");
+ }
+
+ @Override
+ public void setIcon(Resource icon, String iconAltText) {
this.icon = icon;
+ this.iconAltText = iconAltText;
markAsDirty();
}
@Override
+ public String getIconAltText() {
+ return iconAltText;
+ }
+
+ @Override
+ public void setIconAltText(String iconAltText) {
+ this.iconAltText = iconAltText;
+ markAsDirty();
+ }
+
+ @Override
+ public void setDefaultFocusComponent(Focusable defaultFocus) {
+ this.defaultFocus = defaultFocus;
+ }
+
+ @Override
+ public Focusable getDefaultFocusComponent() {
+ return defaultFocus;
+ }
+
+ @Override
public boolean isEnabled() {
return enabled;
}
@@ -1150,6 +1306,18 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
public String getStyleName() {
return styleName;
}
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ markAsDirty();
+
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
}
/**
@@ -1309,7 +1477,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
*/
private static void copyTabMetadata(Tab from, Tab to) {
to.setCaption(from.getCaption());
- to.setIcon(from.getIcon());
+ to.setIcon(from.getIcon(), from.getIconAltText());
to.setDescription(from.getDescription());
to.setVisible(from.isVisible());
to.setEnabled(from.isEnabled());
diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java
index 746fa194ac..0d2ef4d569 100644
--- a/server/src/com/vaadin/ui/UI.java
+++ b/server/src/com/vaadin/ui/UI.java
@@ -77,7 +77,7 @@ import com.vaadin.util.CurrentInstance;
* When a new UI instance is needed, typically because the user opens a URL in a
* browser window which points to e.g. {@link VaadinServlet}, all
* {@link UIProvider}s registered to the current {@link VaadinSession} are
- * queried for the UI class that should be used. The selection is by defaylt
+ * queried for the UI class that should be used. The selection is by default
* based on the <code>UI</code> init parameter from web.xml.
* </p>
* <p>
@@ -222,6 +222,9 @@ public abstract class UI extends AbstractSingleComponentContainer implements
private PushConfiguration pushConfiguration = new PushConfigurationImpl(
this);
+ private NotificationConfiguration notificationConfiguration = new NotificationConfigurationImpl(
+ this);
+
/**
* Creates a new empty UI without a caption. The content of the UI must be
* set by calling {@link #setContent(Component)} before using the UI.
@@ -551,6 +554,8 @@ public abstract class UI extends AbstractSingleComponentContainer implements
private LocaleService localeService = new LocaleService(this,
getState(false).localeServiceState);
+ private String embedId;
+
/**
* This method is used by Component.Focusable objects to request focus to
* themselves. Focus renders must be handled at window level (instead of
@@ -598,12 +603,19 @@ public abstract class UI extends AbstractSingleComponentContainer implements
* the initialization request
* @param uiId
* the id of the new ui
+ * @param embedId
+ * the embed id of this UI, or <code>null</code> if no id is
+ * known
+ *
+ * @see #getUIId()
+ * @see #getEmbedId()
*/
- public void doInit(VaadinRequest request, int uiId) {
+ public void doInit(VaadinRequest request, int uiId, String embedId) {
if (this.uiId != -1) {
throw new IllegalStateException("UI id has already been defined");
}
this.uiId = uiId;
+ this.embedId = embedId;
// Actual theme - used for finding CustomLayout templates
theme = request.getParameter("theme");
@@ -1334,6 +1346,15 @@ public abstract class UI extends AbstractSingleComponentContainer implements
}
/**
+ * Retrieves the object used for configuring notifications.
+ *
+ * @return The instance used for notification configuration
+ */
+ public NotificationConfiguration getNotificationConfiguration() {
+ return notificationConfiguration;
+ }
+
+ /**
* Retrieves the object used for configuring the loading indicator.
*
* @return The instance used for configuring the loading indicator
@@ -1518,4 +1539,18 @@ public abstract class UI extends AbstractSingleComponentContainer implements
private static Logger getLogger() {
return Logger.getLogger(UI.class.getName());
}
+
+ /**
+ * Gets a string the uniquely distinguishes this UI instance based on where
+ * it is embedded. The embed identifier is based on the
+ * <code>window.name</code> DOM attribute of the browser window where the UI
+ * is displayed and the id of the div element where the UI is embedded.
+ *
+ * @since 7.2
+ * @return the embed id for this UI, or <code>null</code> if no id known
+ */
+ public String getEmbedId() {
+ return embedId;
+ }
+
}
diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java
index 5820161c1c..8e7c6dbc80 100644
--- a/server/src/com/vaadin/ui/Window.java
+++ b/server/src/com/vaadin/ui/Window.java
@@ -18,6 +18,9 @@ package com.vaadin.ui;
import java.io.Serializable;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import com.vaadin.event.FieldEvents.BlurEvent;
@@ -33,10 +36,12 @@ import com.vaadin.event.ShortcutAction.ModifierKey;
import com.vaadin.event.ShortcutListener;
import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
+import com.vaadin.shared.Connector;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.ui.window.WindowMode;
import com.vaadin.shared.ui.window.WindowServerRpc;
import com.vaadin.shared.ui.window.WindowState;
+import com.vaadin.shared.ui.window.WindowState.WindowRole;
import com.vaadin.util.ReflectTools;
/**
@@ -228,8 +233,6 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
// Don't do anything if not attached to a UI
if (uI != null) {
- // focus is restored to the parent window
- uI.focus();
// window is removed from the UI
uI.removeWindow(this);
}
@@ -634,7 +637,10 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
/**
* Sets window modality. When a modal window is open, components outside
- * that window it cannot be accessed.
+ * that window cannot be accessed.
+ * <p>
+ * Keyboard navigation is restricted by blocking the tab key at the top and
+ * bottom of the window by activating the tab stop function internally.
*
* @param modal
* true if modality is to be turned on
@@ -995,4 +1001,194 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier,
protected WindowState getState(boolean markAsDirty) {
return (WindowState) super.getState(markAsDirty);
}
+
+ /**
+ * Allows to specify which components contain the description for the
+ * window. Text contained in these components will be read by assistive
+ * devices when it is opened.
+ *
+ * @param connectors
+ * with the components to use as description
+ */
+ public void setAssistiveDescription(Connector... connectors) {
+ if (connectors == null) {
+ throw new IllegalArgumentException(
+ "Parameter connectors must be non-null");
+ } else {
+ getState().contentDescription = connectors;
+ }
+ }
+
+ /**
+ * Gets the components that are used as assistive description. Text
+ * contained in these components will be read by assistive devices when the
+ * window is opened.
+ *
+ * @return list of previously set components
+ */
+ public List<Connector> getAssistiveDescription() {
+ return Collections.unmodifiableList(Arrays
+ .asList(getState().contentDescription));
+ }
+
+ /**
+ * Sets the accessibility prefix for the window caption.
+ *
+ * This prefix is read to assistive device users before the window caption,
+ * but not visible on the page.
+ *
+ * @param prefix
+ * String that is placed before the window caption
+ */
+ public void setAssistivePrefix(String prefix) {
+ getState().assistivePrefix = prefix;
+ }
+
+ /**
+ * Gets the accessibility prefix for the window caption.
+ *
+ * This prefix is read to assistive device users before the window caption,
+ * but not visible on the page.
+ *
+ * @return The accessibility prefix
+ */
+ public String getAssistivePrefix() {
+ return getState().assistivePrefix;
+ }
+
+ /**
+ * Sets the accessibility postfix for the window caption.
+ *
+ * This postfix is read to assistive device users after the window caption,
+ * but not visible on the page.
+ *
+ * @param prefix
+ * String that is placed after the window caption
+ */
+ public void setAssistivePostfix(String assistivePostfix) {
+ getState().assistivePostfix = assistivePostfix;
+ }
+
+ /**
+ * Gets the accessibility postfix for the window caption.
+ *
+ * This postfix is read to assistive device users after the window caption,
+ * but not visible on the page.
+ *
+ * @return The accessibility postfix
+ */
+ public String getAssistivePostfix() {
+ return getState().assistivePostfix;
+ }
+
+ /**
+ * Sets the WAI-ARIA role the window.
+ *
+ * This role defines how an assistive device handles a window. Available
+ * roles are alertdialog and dialog (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * The default role is dialog.
+ *
+ * @param role
+ * WAI-ARIA role to set for the window
+ */
+ public void setAssistiveRole(WindowRole role) {
+ getState().role = role;
+ }
+
+ /**
+ * Gets the WAI-ARIA role the window.
+ *
+ * This role defines how an assistive device handles a window. Available
+ * roles are alertdialog and dialog (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * @return WAI-ARIA role set for the window
+ */
+ public WindowRole getAssistiveRole() {
+ return getState().role;
+ }
+
+ /**
+ * Set if it should be prevented to set the focus to a component outside a
+ * non-modal window with the tab key.
+ * <p>
+ * This is meant to help users of assistive devices to not leaving the
+ * window unintentionally.
+ * <p>
+ * For modal windows, this function is activated automatically, while
+ * preserving the stored value of tabStop.
+ *
+ * @param tabStop
+ * true to keep the focus inside the window when reaching the top
+ * or bottom, false (default) to allow leaving the window
+ */
+ public void setTabStopEnabled(boolean tabStop) {
+ getState().assistiveTabStop = tabStop;
+ }
+
+ /**
+ * Get if it is prevented to leave a window with the tab key.
+ *
+ * @return true when the focus is limited to inside the window, false when
+ * focus can leave the window
+ */
+ public boolean isTabStopEnabled() {
+ return getState().assistiveTabStop;
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param topMessage
+ * String provided when the user navigates with Shift-Tab keys to
+ * the top of the window
+ */
+ public void setTabStopTopAssistiveText(String topMessage) {
+ getState().assistiveTabStopTopText = topMessage;
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param bottomMessage
+ * String provided when the user navigates with the Tab key to
+ * the bottom of the window
+ */
+ public void setTabStopBottomAssistiveText(String bottomMessage) {
+ getState().assistiveTabStopBottomText = bottomMessage;
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ *
+ * @return the top message
+ */
+ public String getTabStopTopAssistiveText() {
+ return getState().assistiveTabStopTopText;
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ *
+ * @return the bottom message
+ */
+ public String getTabStopBottomAssistiveText() {
+ return getState().assistiveTabStopBottomText;
+ }
}
diff --git a/server/tests/src/com/vaadin/data/util/BeanContainerTest.java b/server/tests/src/com/vaadin/data/util/BeanContainerTest.java
index 9037e303a8..2dcbb4aed8 100644
--- a/server/tests/src/com/vaadin/data/util/BeanContainerTest.java
+++ b/server/tests/src/com/vaadin/data/util/BeanContainerTest.java
@@ -457,4 +457,28 @@ public class BeanContainerTest extends AbstractBeanContainerTest {
.getValue());
}
+ public void testNestedContainerPropertyWithNullBean() {
+ BeanContainer<String, NestedMethodPropertyTest.Person> container = new BeanContainer<String, NestedMethodPropertyTest.Person>(
+ NestedMethodPropertyTest.Person.class);
+ container.setBeanIdProperty("name");
+
+ container.addBean(new NestedMethodPropertyTest.Person("John", null));
+ assertTrue(container
+ .addNestedContainerProperty("address.postalCodeObject"));
+ assertTrue(container.addNestedContainerProperty("address.street", true));
+ // the nested properties added with allowNullBean setting should return
+ // null
+ assertNull(container.getContainerProperty("John", "address.street")
+ .getValue());
+ // nested properties added without allowNullBean setting should throw
+ // exception
+ try {
+ container.getContainerProperty("John", "address.postalCodeObject")
+ .getValue();
+ fail();
+ } catch (Exception e) {
+ // should throw exception
+ }
+ }
+
}
diff --git a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java
index 6b88eb336d..35f09fc8f3 100644
--- a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java
+++ b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java
@@ -10,8 +10,15 @@ import java.util.Map;
import junit.framework.Assert;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
import com.vaadin.data.Container;
+import com.vaadin.data.Container.Indexed.ItemAddEvent;
+import com.vaadin.data.Container.Indexed.ItemRemoveEvent;
+import com.vaadin.data.Container.ItemSetChangeListener;
import com.vaadin.data.Item;
+import com.vaadin.data.util.filter.Compare;
/**
* Test basic functionality of BeanItemContainer.
@@ -714,4 +721,205 @@ public class BeanItemContainerTest extends AbstractBeanContainerTest {
.getValue());
}
+ public void testNestedContainerPropertyWithNullBean() {
+ BeanItemContainer<NestedMethodPropertyTest.Person> container = new BeanItemContainer<NestedMethodPropertyTest.Person>(
+ NestedMethodPropertyTest.Person.class);
+ NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person(
+ "John", null);
+ assertNotNull(container.addBean(john));
+ assertTrue(container
+ .addNestedContainerProperty("address.postalCodeObject"));
+ assertTrue(container.addNestedContainerProperty("address.street", true));
+ // the nested properties added with allowNullBean setting should return
+ // null
+ assertNull(container.getContainerProperty(john, "address.street")
+ .getValue());
+ // nested properties added without allowNullBean setting should throw
+ // exception
+ try {
+ container.getContainerProperty(john, "address.postalCodeObject")
+ .getValue();
+ fail();
+ } catch (Exception e) {
+ // should throw exception
+ }
+ }
+
+ public void testItemAddedEvent() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class));
+ EasyMock.replay(addListener);
+
+ container.addItem(bean);
+
+ EasyMock.verify(addListener);
+ }
+
+ public void testItemAddedEvent_AddedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ container.addItem(bean);
+
+ assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemAddedEvent_addItemAt_IndexOfAddedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ container.addItemAt(1, new Person(""));
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemAddedEvent_addItemAfter_IndexOfAddedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ container.addItemAfter(bean, new Person(""));
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemAddedEvent_amountOfAddedItems() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+ List<Person> beans = Arrays.asList(new Person("Jack"), new Person(
+ "John"));
+
+ container.addAll(beans);
+
+ assertEquals(2, capturedEvent.getValue().getAddedItemsCount());
+ }
+
+ public void testItemAddedEvent_someItemsAreFiltered_amountOfAddedItemsIsReducedByAmountOfFilteredItems() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+ List<Person> beans = Arrays.asList(new Person("Jack"), new Person(
+ "John"));
+ container.addFilter(new Compare.Equal("name", "John"));
+
+ container.addAll(beans);
+
+ assertEquals(1, capturedEvent.getValue().getAddedItemsCount());
+ }
+
+ public void testItemAddedEvent_someItemsAreFiltered_addedItemIsTheFirstVisibleItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+ List<Person> beans = Arrays.asList(new Person("Jack"), bean);
+ container.addFilter(new Compare.Equal("name", "John"));
+
+ container.addAll(beans);
+
+ assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemRemovedEvent() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ removeListener.containerItemSetChange(EasyMock
+ .isA(ItemRemoveEvent.class));
+ EasyMock.replay(removeListener);
+
+ container.removeItem(bean);
+
+ EasyMock.verify(removeListener);
+ }
+
+ public void testItemRemovedEvent_RemovedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ Person bean = new Person("John");
+ container.addItem(bean);
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(bean);
+
+ assertEquals(bean, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemRemovedEvent_indexOfRemovedItem() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ container.addItem(new Person("Jack"));
+ Person secondBean = new Person("John");
+ container.addItem(secondBean);
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(secondBean);
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemRemovedEvent_amountOfRemovedItems() {
+ BeanItemContainer<Person> container = new BeanItemContainer<Person>(
+ Person.class);
+ container.addItem(new Person("Jack"));
+ container.addItem(new Person("John"));
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeAllItems();
+
+ assertEquals(2, capturedEvent.getValue().getRemovedItemsCount());
+ }
+
+ private Capture<ItemAddEvent> captureAddEvent(
+ ItemSetChangeListener addListener) {
+ Capture<ItemAddEvent> capturedEvent = new Capture<ItemAddEvent>();
+ addListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private Capture<ItemRemoveEvent> captureRemoveEvent(
+ ItemSetChangeListener removeListener) {
+ Capture<ItemRemoveEvent> capturedEvent = new Capture<ItemRemoveEvent>();
+ removeListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private ItemSetChangeListener createListenerMockFor(
+ BeanItemContainer<Person> container) {
+ ItemSetChangeListener listener = EasyMock
+ .createNiceMock(ItemSetChangeListener.class);
+ container.addItemSetChangeListener(listener);
+ return listener;
+ }
}
diff --git a/server/tests/src/com/vaadin/data/util/NestedMethodPropertyTest.java b/server/tests/src/com/vaadin/data/util/NestedMethodPropertyTest.java
index 640ede8743..d517322010 100644
--- a/server/tests/src/com/vaadin/data/util/NestedMethodPropertyTest.java
+++ b/server/tests/src/com/vaadin/data/util/NestedMethodPropertyTest.java
@@ -273,6 +273,23 @@ public class NestedMethodPropertyTest extends TestCase {
Assert.assertEquals("Joonas", managerNameProperty.getValue());
}
+ public void testNullNestedPropertyWithAllowNullBeans() {
+ NestedMethodProperty<String> managerNameProperty = new NestedMethodProperty<String>(
+ vaadin, "manager.name", true);
+ NestedMethodProperty<String> streetProperty = new NestedMethodProperty<String>(
+ vaadin, "manager.address.street", true);
+
+ joonas.setAddress(null);
+ // should return null
+ Assert.assertNull(streetProperty.getValue());
+
+ vaadin.setManager(null);
+ Assert.assertNull(managerNameProperty.getValue());
+ vaadin.setManager(joonas);
+ Assert.assertEquals("Joonas", managerNameProperty.getValue());
+ Assert.assertNull(streetProperty.getValue());
+ }
+
public void testMultiLevelNestedPropertySetValue() {
NestedMethodProperty<String> managerNameProperty = new NestedMethodProperty<String>(
vaadin, "manager.name");
@@ -314,6 +331,20 @@ public class NestedMethodPropertyTest extends TestCase {
Assert.assertEquals("Ruukinkatu 2-4", property2.getValue());
}
+ public void testSerializationWithNullBeansAllowed() throws IOException,
+ ClassNotFoundException {
+ vaadin.setManager(null);
+ NestedMethodProperty<String> streetProperty = new NestedMethodProperty<String>(
+ vaadin, "manager.address.street", true);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new ObjectOutputStream(baos).writeObject(streetProperty);
+ @SuppressWarnings("unchecked")
+ NestedMethodProperty<String> property2 = (NestedMethodProperty<String>) new ObjectInputStream(
+ new ByteArrayInputStream(baos.toByteArray())).readObject();
+
+ Assert.assertNull(property2.getValue());
+ }
+
public void testIsReadOnly() {
NestedMethodProperty<String> streetProperty = new NestedMethodProperty<String>(
vaadin, "manager.address.street");
diff --git a/server/tests/src/com/vaadin/data/util/PropertyDescriptorTest.java b/server/tests/src/com/vaadin/data/util/PropertyDescriptorTest.java
index 14e70d76d4..0ae76430f6 100644
--- a/server/tests/src/com/vaadin/data/util/PropertyDescriptorTest.java
+++ b/server/tests/src/com/vaadin/data/util/PropertyDescriptorTest.java
@@ -52,4 +52,20 @@ public class PropertyDescriptorTest extends TestCase {
Property<?> property = pd2.createProperty(new Person("John", null));
Assert.assertEquals("John", property.getValue());
}
+
+ public void testNestedPropertyDescriptorWithNullBeansAllowedSerialization()
+ throws Exception {
+ NestedPropertyDescriptor<Person> pd = new NestedPropertyDescriptor<Person>(
+ "address.street", Person.class, true);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new ObjectOutputStream(baos).writeObject(pd);
+ @SuppressWarnings("unchecked")
+ VaadinPropertyDescriptor<Person> pd2 = (VaadinPropertyDescriptor<Person>) new ObjectInputStream(
+ new ByteArrayInputStream(baos.toByteArray())).readObject();
+
+ Property<?> property = pd2.createProperty(new Person("John", null));
+ Assert.assertNull(property.getValue());
+ }
+
}
diff --git a/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java b/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java
index 09e5a26c15..5c78965092 100644
--- a/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java
+++ b/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java
@@ -4,6 +4,12 @@ import java.util.List;
import junit.framework.Assert;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
+import com.vaadin.data.Container.Indexed.ItemAddEvent;
+import com.vaadin.data.Container.Indexed.ItemRemoveEvent;
+import com.vaadin.data.Container.ItemSetChangeListener;
import com.vaadin.data.Item;
public class TestIndexedContainer extends AbstractInMemoryContainerTest {
@@ -271,6 +277,113 @@ public class TestIndexedContainer extends AbstractInMemoryContainerTest {
counter.assertNone();
}
+ public void testItemAddedEvent() {
+ IndexedContainer container = new IndexedContainer();
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class));
+ EasyMock.replay(addListener);
+
+ container.addItem();
+
+ EasyMock.verify(addListener);
+ }
+
+ public void testItemAddedEvent_AddedItem() {
+ IndexedContainer container = new IndexedContainer();
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ Object itemId = container.addItem();
+
+ assertEquals(itemId, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemAddedEvent_IndexOfAddedItem() {
+ IndexedContainer container = new IndexedContainer();
+ ItemSetChangeListener addListener = createListenerMockFor(container);
+ container.addItem();
+ Capture<ItemAddEvent> capturedEvent = captureAddEvent(addListener);
+ EasyMock.replay(addListener);
+
+ Object itemId = container.addItemAt(1);
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemRemovedEvent() {
+ IndexedContainer container = new IndexedContainer();
+ Object itemId = container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ removeListener.containerItemSetChange(EasyMock
+ .isA(ItemRemoveEvent.class));
+ EasyMock.replay(removeListener);
+
+ container.removeItem(itemId);
+
+ EasyMock.verify(removeListener);
+ }
+
+ public void testItemRemovedEvent_RemovedItem() {
+ IndexedContainer container = new IndexedContainer();
+ Object itemId = container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(itemId);
+
+ assertEquals(itemId, capturedEvent.getValue().getFirstItemId());
+ }
+
+ public void testItemRemovedEvent_indexOfRemovedItem() {
+ IndexedContainer container = new IndexedContainer();
+ container.addItem();
+ Object secondItemId = container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeItem(secondItemId);
+
+ assertEquals(1, capturedEvent.getValue().getFirstIndex());
+ }
+
+ public void testItemRemovedEvent_amountOfRemovedItems() {
+ IndexedContainer container = new IndexedContainer();
+ container.addItem();
+ container.addItem();
+ ItemSetChangeListener removeListener = createListenerMockFor(container);
+ Capture<ItemRemoveEvent> capturedEvent = captureRemoveEvent(removeListener);
+ EasyMock.replay(removeListener);
+
+ container.removeAllItems();
+
+ assertEquals(2, capturedEvent.getValue().getRemovedItemsCount());
+ }
+
+ private Capture<ItemAddEvent> captureAddEvent(
+ ItemSetChangeListener addListener) {
+ Capture<ItemAddEvent> capturedEvent = new Capture<ItemAddEvent>();
+ addListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private Capture<ItemRemoveEvent> captureRemoveEvent(
+ ItemSetChangeListener removeListener) {
+ Capture<ItemRemoveEvent> capturedEvent = new Capture<ItemRemoveEvent>();
+ removeListener.containerItemSetChange(EasyMock.capture(capturedEvent));
+ return capturedEvent;
+ }
+
+ private ItemSetChangeListener createListenerMockFor(
+ IndexedContainer container) {
+ ItemSetChangeListener listener = EasyMock
+ .createNiceMock(ItemSetChangeListener.class);
+ container.addItemSetChangeListener(listener);
+ return listener;
+ }
+
// Ticket 8028
public void testGetItemIdsRangeIndexOutOfBounds() {
IndexedContainer ic = new IndexedContainer();
diff --git a/server/tests/src/com/vaadin/server/VaadinSessionTest.java b/server/tests/src/com/vaadin/server/VaadinSessionTest.java
index 68f198410c..51ae2a2d13 100644
--- a/server/tests/src/com/vaadin/server/VaadinSessionTest.java
+++ b/server/tests/src/com/vaadin/server/VaadinSessionTest.java
@@ -100,7 +100,7 @@ public class VaadinSessionTest {
}
};
- ui.doInit(vaadinRequest, session.getNextUIid());
+ ui.doInit(vaadinRequest, session.getNextUIid(), null);
ui.setSession(session);
session.addUI(ui);
diff --git a/server/tests/src/com/vaadin/tests/data/bean/BeanToValidate.java b/server/tests/src/com/vaadin/tests/data/bean/BeanToValidate.java
index 416563baba..034609764f 100644
--- a/server/tests/src/com/vaadin/tests/data/bean/BeanToValidate.java
+++ b/server/tests/src/com/vaadin/tests/data/bean/BeanToValidate.java
@@ -4,6 +4,7 @@ import javax.validation.constraints.Digits;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class BeanToValidate {
@@ -21,6 +22,10 @@ public class BeanToValidate {
@Digits(integer = 3, fraction = 2)
private String decimals;
+ @Pattern(regexp = "V*", message = "Must start with letter V")
+ @Size(min = 3, max = 6, message = "Must contain 3 - 6 letters")
+ private String nickname;
+
public String getFirstname() {
return firstname;
}
@@ -53,4 +58,12 @@ public class BeanToValidate {
this.decimals = decimals;
}
+ public String getNickname() {
+ return nickname;
+ }
+
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
+ }
+
}
diff --git a/server/tests/src/com/vaadin/tests/data/bean/PersonWithBeanValidationAnnotations.java b/server/tests/src/com/vaadin/tests/data/bean/PersonWithBeanValidationAnnotations.java
index 93b2273263..575730d946 100644
--- a/server/tests/src/com/vaadin/tests/data/bean/PersonWithBeanValidationAnnotations.java
+++ b/server/tests/src/com/vaadin/tests/data/bean/PersonWithBeanValidationAnnotations.java
@@ -8,12 +8,15 @@ import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
+import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class PersonWithBeanValidationAnnotations {
@NotNull
@Size(min = 5, max = 20)
+ @Pattern(regexp = "A.*")
private String firstName;
+
@NotNull
private String lastName;
diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToBigDecimalConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToBigDecimalConverter.java
new file mode 100644
index 0000000000..5db33691b6
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/data/converter/TestStringToBigDecimalConverter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.data.converter;
+
+import java.math.BigDecimal;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import com.vaadin.data.util.converter.StringToBigDecimalConverter;
+
+public class TestStringToBigDecimalConverter extends TestCase {
+
+ StringToBigDecimalConverter converter = new StringToBigDecimalConverter();
+
+ public void testNullConversion() {
+ assertEquals(null,
+ converter.convertToModel(null, BigDecimal.class, null));
+ }
+
+ public void testEmptyStringConversion() {
+ assertEquals(null, converter.convertToModel("", BigDecimal.class, null));
+ }
+
+ public void testValueParsing() {
+ BigDecimal converted = converter.convertToModel("10", BigDecimal.class,
+ null);
+ BigDecimal expected = new BigDecimal(10);
+ assertEquals(expected, converted);
+ }
+
+ public void testValueFormatting() {
+ BigDecimal bd = new BigDecimal(12.5);
+ String expected = "12,5";
+
+ String converted = converter.convertToPresentation(bd, String.class,
+ Locale.GERMAN);
+ assertEquals(expected, converted);
+ }
+}
diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToLongConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToLongConverter.java
new file mode 100644
index 0000000000..18e2ed06c0
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/data/converter/TestStringToLongConverter.java
@@ -0,0 +1,68 @@
+package com.vaadin.tests.data.converter;
+
+import java.util.Locale;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ReverseConverter;
+import com.vaadin.data.util.converter.StringToLongConverter;
+
+public class TestStringToLongConverter extends TestCase {
+
+ StringToLongConverter converter = new StringToLongConverter();
+ Converter<Long, String> reverseConverter = new ReverseConverter<Long, String>(
+ converter);
+
+ public void testNullConversion() {
+ assertEquals(null, converter.convertToModel(null, Long.class, null));
+ }
+
+ public void testReverseNullConversion() {
+ assertEquals(null,
+ reverseConverter.convertToModel(null, String.class, null));
+ }
+
+ public void testEmptyStringConversion() {
+ assertEquals(null, converter.convertToModel("", Long.class, null));
+ }
+
+ public void testValueConversion() {
+ assertEquals(Long.valueOf(10),
+ converter.convertToModel("10", Long.class, null));
+ }
+
+ public void testReverseValueConversion() {
+ assertEquals(reverseConverter.convertToModel(10L, String.class, null),
+ "10");
+ }
+
+ public void testExtremeLongValueConversion() {
+ long l = converter.convertToModel("9223372036854775807", Long.class,
+ null);
+ Assert.assertEquals(Long.MAX_VALUE, l);
+ l = converter.convertToModel("-9223372036854775808", Long.class, null);
+ assertEquals(Long.MIN_VALUE, l);
+ }
+
+ public void testExtremeReverseLongValueConversion() {
+ String str = reverseConverter.convertToModel(Long.MAX_VALUE,
+ String.class, Locale.ENGLISH);
+ Assert.assertEquals("9,223,372,036,854,775,807", str);
+ str = reverseConverter.convertToModel(Long.MIN_VALUE, String.class,
+ null);
+ Assert.assertEquals("-9,223,372,036,854,775,808", str);
+ }
+
+ public void testOutOfBoundsValueConversion() {
+ // Long.MAX_VALUE+1 is converted to Long.MAX_VALUE
+ long l = converter.convertToModel("9223372036854775808", Long.class,
+ null);
+ Assert.assertEquals(Long.MAX_VALUE, l);
+ // Long.MIN_VALUE-1 is converted to Long.MIN_VALUE
+ l = converter.convertToModel("-9223372036854775809", Long.class, null);
+ assertEquals(Long.MIN_VALUE, l);
+
+ }
+}
diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToNumberConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToNumberConverter.java
deleted file mode 100644
index 66fc4f6532..0000000000
--- a/server/tests/src/com/vaadin/tests/data/converter/TestStringToNumberConverter.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.vaadin.tests.data.converter;
-
-import junit.framework.TestCase;
-
-import com.vaadin.data.util.converter.StringToNumberConverter;
-
-public class TestStringToNumberConverter extends TestCase {
-
- StringToNumberConverter converter = new StringToNumberConverter();
-
- public void testNullConversion() {
- assertEquals(null, converter.convertToModel(null, Number.class, null));
- }
-
- public void testEmptyStringConversion() {
- assertEquals(null, converter.convertToModel("", Number.class, null));
- }
-
- public void testValueConversion() {
- assertEquals(Long.valueOf(10),
- converter.convertToModel("10", Number.class, null));
- assertEquals(10.5, converter.convertToModel("10.5", Number.class, null));
- }
-}
diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java
index a5e825bddb..85116dd152 100644
--- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java
+++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbsFieldValueConversions.java
@@ -205,14 +205,15 @@ public class AbsFieldValueConversions extends TestCase {
}
+ // Now specific to Integer because StringToNumberConverter has been removed
public static class NumberBean {
- private Number number;
+ private Integer number;
- public Number getNumber() {
+ public Integer getNumber() {
return number;
}
- public void setNumber(Number number) {
+ public void setNumber(Integer number) {
this.number = number;
}
@@ -239,7 +240,7 @@ public class AbsFieldValueConversions extends TestCase {
tf.setPropertyDataSource(new MethodProperty<Number>(nb, "number"));
Converter c2 = tf.getConverter();
assertTrue(
- "StringToNumber converter is ok for integer types and should stay even though property is changed",
+ "StringToInteger converter is ok for integer types and should stay even though property is changed",
c1 == c2);
assertEquals(490, tf.getPropertyDataSource().getValue());
assertEquals("490", tf.getValue());
diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java
index bac024725f..99397e9e8f 100644
--- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java
+++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java
@@ -43,6 +43,33 @@ public class DefaultConverterFactory extends TestCase {
}
+ public static class LongBean {
+ long l1;
+ Long l2;
+
+ public LongBean(long l1, Long l2) {
+ this.l1 = l1;
+ this.l2 = l2;
+ }
+
+ public long getL1() {
+ return l1;
+ }
+
+ public void setL1(long l1) {
+ this.l1 = l1;
+ }
+
+ public Long getL2() {
+ return l2;
+ }
+
+ public void setL2(Long l2) {
+ this.l2 = l2;
+ }
+
+ }
+
Person paulaBean = new Person("Paula", "Brilliant", "paula@brilliant.com",
34, Sex.FEMALE, new Address("Paula street 1", 12345, "P-town",
Country.FINLAND));
@@ -68,6 +95,21 @@ public class DefaultConverterFactory extends TestCase {
assertEquals(24f, tf.getPropertyDataSource().getValue());
}
+ public void testLongConversion() {
+ VaadinSession sess = new AlwaysLockedVaadinSession(null);
+ VaadinSession.setCurrent(sess);
+
+ TextField tf = new TextField();
+ tf.setLocale(new Locale("en", "US"));
+ tf.setPropertyDataSource(new MethodProperty<Integer>(new LongBean(12,
+ 1982739187238L), "l2"));
+ assertEquals("1,982,739,187,238", tf.getValue());
+ tf.setValue("1982739187239");
+ assertEquals("1,982,739,187,239", tf.getValue());
+ assertEquals(1982739187239L, tf.getConvertedValue());
+ assertEquals(1982739187239L, tf.getPropertyDataSource().getValue());
+ }
+
public void testDefaultNumberConversion() {
VaadinSession app = new AlwaysLockedVaadinSession(null);
VaadinSession.setCurrent(app);
diff --git a/server/tests/src/com/vaadin/tests/server/validation/TestBeanValidation.java b/server/tests/src/com/vaadin/tests/server/validation/TestBeanValidation.java
index e1d08a989b..1d1a3c297e 100644
--- a/server/tests/src/com/vaadin/tests/server/validation/TestBeanValidation.java
+++ b/server/tests/src/com/vaadin/tests/server/validation/TestBeanValidation.java
@@ -1,7 +1,6 @@
package com.vaadin.tests.server.validation;
-import junit.framework.Assert;
-
+import org.junit.Assert;
import org.junit.Test;
import com.vaadin.data.Validator.InvalidValueException;
@@ -59,6 +58,32 @@ public class TestBeanValidation {
}
@Test
+ public void testBeanValidationException_OneValidationError() {
+ InvalidValueException[] causes = null;
+ BeanValidator validator = new BeanValidator(BeanToValidate.class,
+ "lastname");
+ try {
+ validator.validate(null);
+ } catch (InvalidValueException e) {
+ causes = e.getCauses();
+ }
+
+ Assert.assertEquals(1, causes.length);
+ }
+
+ @Test
+ public void testBeanValidationsException_TwoValidationErrors() {
+ InvalidValueException[] causes = null;
+ BeanValidator validator = new BeanValidator(BeanToValidate.class,
+ "nickname");
+ try {
+ validator.validate("A");
+ } catch (InvalidValueException e) {
+ causes = e.getCauses();
+ }
+
+ Assert.assertEquals(2, causes.length);
+ }
public void testBeanValidationNotAddedTwice() {
// See ticket #11045
BeanFieldGroup<BeanToValidate> fieldGroup = new BeanFieldGroup<BeanToValidate>(
diff --git a/shared/ivy.xml b/shared/ivy.xml
index 3b044e9ab4..fc54d680fa 100644
--- a/shared/ivy.xml
+++ b/shared/ivy.xml
@@ -21,7 +21,7 @@
</publications>
<dependencies>
<dependency org="com.vaadin" name="vaadin-shared-deps"
- rev="1.0.2" conf="build,ide,test->default" />
+ rev="1.0.3" conf="build,ide,test->default" />
</dependencies>
</ivy-module>
diff --git a/shared/src/com/vaadin/shared/ApplicationConstants.java b/shared/src/com/vaadin/shared/ApplicationConstants.java
index 4b5dc9140b..a81f29b77c 100644
--- a/shared/src/com/vaadin/shared/ApplicationConstants.java
+++ b/shared/src/com/vaadin/shared/ApplicationConstants.java
@@ -94,4 +94,26 @@ public class ApplicationConstants implements Serializable {
* Name of the parameter used to transmit the CSRF token.
*/
public static final String CSRF_TOKEN_PARAMETER = "v-csrfToken";
+
+ /**
+ * The name of the parameter used to transmit RPC invocations
+ *
+ * @since 7.2
+ */
+ public static final String RPC_INVOCATIONS = "rpc";
+
+ /**
+ * The name of the parameter used to transmit the CSRF token
+ *
+ * @since 7.2
+ */
+ public static final String CSRF_TOKEN = "csrfToken";
+
+ /**
+ * The name of the parameter used to transmit the sync id
+ *
+ * @see com.vaadin.ui.ConnectorTracker#getCurrentSyncId()
+ * @since 7.2
+ */
+ public static final String SERVER_SYNC_ID = "syncId";
}
diff --git a/shared/src/com/vaadin/shared/Position.java b/shared/src/com/vaadin/shared/Position.java
index cd34ee8b87..3df42d65d8 100755
--- a/shared/src/com/vaadin/shared/Position.java
+++ b/shared/src/com/vaadin/shared/Position.java
@@ -16,5 +16,10 @@
package com.vaadin.shared;
public enum Position {
- TOP_LEFT, TOP_CENTER, TOP_RIGHT, MIDDLE_LEFT, MIDDLE_CENTER, MIDDLE_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT;
+ TOP_LEFT, TOP_CENTER, TOP_RIGHT, MIDDLE_LEFT, MIDDLE_CENTER, MIDDLE_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT,
+ /**
+ * Position that is only accessible for assistive devices, invisible for
+ * visual users.
+ **/
+ ASSISTIVE;
}
diff --git a/shared/src/com/vaadin/shared/ui/link/LinkState.java b/shared/src/com/vaadin/shared/ui/link/LinkState.java
index 269496767d..33ede86378 100644
--- a/shared/src/com/vaadin/shared/ui/link/LinkState.java
+++ b/shared/src/com/vaadin/shared/ui/link/LinkState.java
@@ -16,9 +16,15 @@
package com.vaadin.shared.ui.link;
import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.ui.BorderStyle;
public class LinkState extends AbstractComponentState {
{
primaryStyleName = "v-link";
}
+ public String name = "";
+ public String target = null;
+ public BorderStyle targetBorder = BorderStyle.DEFAULT;
+ public int targetWidth = -1;
+ public int targetHeight = -1;
}
diff --git a/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetBaseConstants.java b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetBaseConstants.java
index 7eb23a9887..b7f337a5a8 100644
--- a/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetBaseConstants.java
+++ b/shared/src/com/vaadin/shared/ui/tabsheet/TabsheetBaseConstants.java
@@ -29,5 +29,7 @@ public class TabsheetBaseConstants implements Serializable {
public static final String ATTRIBUTE_TAB_CAPTION = "caption";
@Deprecated
public static final String ATTRIBUTE_TAB_ICON = "icon";
+ @Deprecated
+ public static final String ATTRIBUTE_TAB_ICON_ALT = "iconalt";
}
diff --git a/shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java b/shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java
new file mode 100644
index 0000000000..05a1706763
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ *
+ */
+package com.vaadin.shared.ui.ui;
+
+import java.io.Serializable;
+
+/**
+ * Holds configuration information for a notification type.
+ *
+ * @author Vaadin Ltd
+ */
+public class NotificationConfigurationBean implements Serializable {
+ /**
+ * Available WAI-ARIA roles for a notification.
+ */
+ public enum Role {
+ ALERT, STATUS
+ };
+
+ private String prefix;
+ private String postfix;
+ private Role role = Role.ALERT;
+
+ public NotificationConfigurationBean() {
+ }
+
+ public NotificationConfigurationBean(String prefix, String postfix,
+ Role role) {
+ this.prefix = prefix;
+ this.postfix = postfix;
+ this.role = role;
+ }
+
+ /**
+ * Returns the accessibility prefix, which is placed before the notification
+ * content.
+ *
+ * @return the prefix
+ */
+ public String getAssistivePrefix() {
+ return prefix;
+ }
+
+ /**
+ * Sets the accessibility prefix, which is placed before the notification
+ * content.
+ *
+ * @param pefix
+ * the prefix to set
+ */
+ public void setAssistivePrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * Checks if an accessibility prefix is set.
+ *
+ * @return true when assistivePrefix is not null and has a length > 0, false
+ * otherwise
+ */
+ public boolean hasAssistivePrefix() {
+ return prefix != null && !prefix.isEmpty();
+ }
+
+ /**
+ * Returns the accessibility postfix, which is placed after the notification
+ * content.
+ *
+ * @return the postfix
+ */
+ public String getAssistivePostfix() {
+ return postfix;
+ }
+
+ /**
+ * Sets the accessibility postfix, which is placed after the notification
+ * content.
+ *
+ * @param postfix
+ * the postfix to set
+ */
+ public void setAssistivePostfix(String postfix) {
+ this.postfix = postfix;
+ }
+
+ /**
+ * Checks if an accessibility postfix is set.
+ *
+ * @return true when postfix is not null and has a length > 0, false
+ * otherwise
+ */
+ public boolean hasAssistivePostfix() {
+ return postfix != null && !postfix.isEmpty();
+ }
+
+ /**
+ * Returns the WAI-ARIA role that defines how an assistive device will
+ * inform the user about a notification.
+ *
+ * @return the role
+ */
+ public Role getAssistiveRole() {
+ return role;
+ }
+
+ /**
+ * Sets the WAI-ARIA role that defines how an assistive device will inform
+ * the user about a notification.
+ *
+ * Available roles are alert, alertdialog and status (@see <a
+ * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles
+ * Model</a>).
+ *
+ * @param role
+ * the role to set
+ */
+ public void setAssistiveRole(Role role) {
+ this.role = role;
+ }
+}
diff --git a/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java b/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java
index eb847bacd0..76a3fbfdb6 100644
--- a/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java
+++ b/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java
@@ -20,8 +20,6 @@ import com.vaadin.shared.communication.ClientRpc;
public interface PageClientRpc extends ClientRpc {
- public void setTitle(String title);
-
public void reload();
}
diff --git a/shared/src/com/vaadin/shared/ui/ui/PageState.java b/shared/src/com/vaadin/shared/ui/ui/PageState.java
index 0b51eb4bba..ded73d16d5 100644
--- a/shared/src/com/vaadin/shared/ui/ui/PageState.java
+++ b/shared/src/com/vaadin/shared/ui/ui/PageState.java
@@ -30,4 +30,9 @@ public class PageState implements Serializable {
* True if the page has browser window resize listeners.
*/
public boolean hasResizeListeners = false;
+
+ /**
+ * Non-null if the title is set.
+ */
+ public String title = null;
} \ No newline at end of file
diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java
index 8d042a835f..4cde452a2b 100644
--- a/shared/src/com/vaadin/shared/ui/ui/UIState.java
+++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java
@@ -23,10 +23,12 @@ import java.util.Map;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.TabIndexState;
+import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role;
public class UIState extends TabIndexState {
public TooltipConfigurationState tooltipConfiguration = new TooltipConfigurationState();
public LoadingIndicatorConfigurationState loadingIndicatorConfiguration = new LoadingIndicatorConfigurationState();
+ public NotificationConfigurationState notificationConfiguration = new NotificationConfigurationState();
public int pollInterval = -1;
// Informing users of assistive devices, that the content of this container
@@ -48,6 +50,22 @@ public class UIState extends TabIndexState {
public int maxWidth = 500;
}
+ public static class NotificationConfigurationState implements Serializable {
+ public Map<String, NotificationConfigurationBean> setup = new HashMap<String, NotificationConfigurationBean>();
+ {
+ setup.put("error", new NotificationConfigurationBean("Error: ",
+ " - close with ESC-key", Role.ALERT));
+ setup.put("warning", new NotificationConfigurationBean("Warning: ",
+ null, Role.ALERT));
+ setup.put("humanized", new NotificationConfigurationBean("Info: ",
+ null, Role.ALERT));
+ setup.put("tray", new NotificationConfigurationBean("Status: ",
+ null, Role.STATUS));
+ setup.put("assistive", new NotificationConfigurationBean("Note: ",
+ null, Role.STATUS));
+ };
+ }
+
public static class PushConfigurationState implements Serializable {
public static final String TRANSPORT_PARAM = "transport";
public static final String FALLBACK_TRANSPORT_PARAM = "fallbackTransport";
diff --git a/shared/src/com/vaadin/shared/ui/window/WindowState.java b/shared/src/com/vaadin/shared/ui/window/WindowState.java
index 5a2d2b81b0..3cb6bbe61e 100644
--- a/shared/src/com/vaadin/shared/ui/window/WindowState.java
+++ b/shared/src/com/vaadin/shared/ui/window/WindowState.java
@@ -15,6 +15,7 @@
*/
package com.vaadin.shared.ui.window;
+import com.vaadin.shared.Connector;
import com.vaadin.shared.ui.panel.PanelState;
public class WindowState extends PanelState {
@@ -22,6 +23,13 @@ public class WindowState extends PanelState {
primaryStyleName = "v-window";
}
+ /**
+ * Available WAI-ARIA roles for a window.
+ */
+ public enum WindowRole {
+ ALERTDIALOG, DIALOG
+ };
+
public boolean modal = false;
public boolean resizable = true;
public boolean resizeLazy = false;
@@ -30,4 +38,12 @@ public class WindowState extends PanelState {
public int positionX = -1;
public int positionY = -1;
public WindowMode windowMode = WindowMode.NORMAL;
+
+ public String assistivePrefix = "";
+ public String assistivePostfix = "";
+ public Connector[] contentDescription = new Connector[0];
+ public WindowRole role = WindowRole.DIALOG;
+ public boolean assistiveTabStop = false;
+ public String assistiveTabStopTopText = "Top of dialog";
+ public String assistiveTabStopBottomText = "Bottom of Dialog";
} \ No newline at end of file
diff --git a/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java b/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java
index dbb3e571dc..7213553e18 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java
@@ -19,10 +19,10 @@ package com.vaadin.sass.internal;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
@@ -35,8 +35,9 @@ import com.vaadin.sass.internal.handler.SCSSErrorHandler;
import com.vaadin.sass.internal.parser.ParseException;
import com.vaadin.sass.internal.parser.Parser;
import com.vaadin.sass.internal.parser.SCSSParseException;
+import com.vaadin.sass.internal.resolver.ClassloaderResolver;
+import com.vaadin.sass.internal.resolver.FilesystemResolver;
import com.vaadin.sass.internal.resolver.ScssStylesheetResolver;
-import com.vaadin.sass.internal.resolver.VaadinResolver;
import com.vaadin.sass.internal.tree.BlockNode;
import com.vaadin.sass.internal.tree.MixinDefNode;
import com.vaadin.sass.internal.tree.Node;
@@ -59,10 +60,12 @@ public class ScssStylesheet extends Node {
private static HashMap<Node, Node> lastNodeAdded = new HashMap<Node, Node>();
- private String fileName;
+ private File file;
private String charset;
+ private List<ScssStylesheetResolver> resolvers = new ArrayList<ScssStylesheetResolver>();
+
/**
* Read in a file SCSS and parse it into a ScssStylesheet
*
@@ -101,8 +104,8 @@ public class ScssStylesheet extends Node {
* @throws CSSException
* @throws IOException
*/
- public static ScssStylesheet get(String identifier, String encoding)
- throws CSSException, IOException {
+ public static ScssStylesheet get(String identifier,
+ ScssStylesheet parentStylesheet) throws CSSException, IOException {
/*
* The encoding to be used is passed through "encoding" parameter. the
* imported children scss node will have the same encoding as their
@@ -122,12 +125,22 @@ public class ScssStylesheet extends Node {
SCSSDocumentHandler handler = new SCSSDocumentHandlerImpl();
ScssStylesheet stylesheet = handler.getStyleSheet();
-
- InputSource source = stylesheet.resolveStylesheet(identifier);
+ if (parentStylesheet == null) {
+ // Use default resolvers
+ stylesheet.addResolver(new FilesystemResolver());
+ stylesheet.addResolver(new ClassloaderResolver());
+ } else {
+ // Use parent resolvers
+ stylesheet.setResolvers(parentStylesheet.getResolvers());
+ }
+ InputSource source = stylesheet.resolveStylesheet(identifier,
+ parentStylesheet);
if (source == null) {
return null;
}
- source.setEncoding(encoding);
+ if (parentStylesheet != null) {
+ source.setEncoding(parentStylesheet.getCharset());
+ }
Parser parser = new Parser();
parser.setErrorHandler(new SCSSErrorHandler());
@@ -145,24 +158,13 @@ public class ScssStylesheet extends Node {
return stylesheet;
}
- private static ScssStylesheetResolver[] resolvers = null;
-
- public static void setStylesheetResolvers(
- ScssStylesheetResolver... styleSheetResolvers) {
- resolvers = Arrays.copyOf(styleSheetResolvers,
- styleSheetResolvers.length);
- }
-
- public InputSource resolveStylesheet(String identifier) {
- if (resolvers == null) {
- setStylesheetResolvers(new VaadinResolver());
- }
-
- for (ScssStylesheetResolver resolver : resolvers) {
- InputSource source = resolver.resolve(identifier);
+ public InputSource resolveStylesheet(String identifier,
+ ScssStylesheet parentStylesheet) {
+ for (ScssStylesheetResolver resolver : getResolvers()) {
+ InputSource source = resolver.resolve(parentStylesheet, identifier);
if (source != null) {
File f = new File(source.getURI());
- setFileName(f.getParent());
+ setFile(f);
return source;
}
}
@@ -171,6 +173,38 @@ public class ScssStylesheet extends Node {
}
/**
+ * Retrieves a list of resolvers to use when resolving imports
+ *
+ * @since 7.2
+ * @return the resolvers used to resolving imports
+ */
+ public List<ScssStylesheetResolver> getResolvers() {
+ return Collections.unmodifiableList(resolvers);
+ }
+
+ /**
+ * Sets the list of resolvers to use when resolving imports
+ *
+ * @since 7.2
+ * @param resolvers
+ * the resolvers to set
+ */
+ public void setResolvers(List<ScssStylesheetResolver> resolvers) {
+ this.resolvers = new ArrayList<ScssStylesheetResolver>(resolvers);
+ }
+
+ /**
+ * Adds the given resolver to the resolver list
+ *
+ * @since 7.2
+ * @param resolver
+ * The resolver to add
+ */
+ public void addResolver(ScssStylesheetResolver resolver) {
+ resolvers.add(resolver);
+ }
+
+ /**
* Applies all the visitors and compiles SCSS into Css.
*
* @throws Exception
@@ -355,12 +389,28 @@ public class ScssStylesheet extends Node {
return mixinDefs.get(name);
}
- public void setFileName(String fileName) {
- this.fileName = fileName;
+ public void setFile(File file) {
+ this.file = file;
}
+ /**
+ * Returns the directory containing this style sheet
+ *
+ * @since 7.2
+ * @return The directory containing this style sheet
+ */
+ public String getDirectory() {
+ return file.getParent();
+ }
+
+ /**
+ * Returns the full file name for this style sheet
+ *
+ * @since 7.2
+ * @return The full file name for this style sheet
+ */
public String getFileName() {
- return fileName;
+ return file.getPath();
}
public static HashMap<Node, Node> getLastNodeAdded() {
diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java
index d7662d35a8..d7662d35a8 100755..100644
--- a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java
diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java
new file mode 100644
index 0000000000..5de1f95264
--- /dev/null
+++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.sass.internal.resolver;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import org.w3c.css.sac.InputSource;
+
+import com.vaadin.sass.internal.ScssStylesheet;
+
+/**
+ * Base class for resolvers. Implements functionality for locating paths which
+ * an import can be relative to and helpers for extracting path information from
+ * the identifier.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public abstract class AbstractResolver implements ScssStylesheetResolver,
+ Serializable {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.sass.internal.resolver.ScssStylesheetResolver#resolve(java
+ * .lang.String)
+ */
+ @Override
+ public InputSource resolve(ScssStylesheet parentStylesheet,
+ String identifier) {
+ // Remove a possible ".scss" suffix
+ identifier = identifier.replaceFirst(".scss$", "");
+
+ List<String> potentialParentPaths = getPotentialParentPaths(
+ parentStylesheet, identifier);
+
+ // remove path from identifier as it has already been added to the
+ // parent path
+ if (identifier.contains("/")) {
+ identifier = identifier.substring(identifier.lastIndexOf("/") + 1);
+ }
+
+ for (String path : potentialParentPaths) {
+ InputSource source = normalizeAndResolve(path + "/" + identifier);
+
+ if (source != null) {
+ return source;
+ }
+
+ // Try to find partial import (_identifier.scss)
+ source = normalizeAndResolve(path + "/_" + identifier);
+
+ if (source != null) {
+ return source;
+ }
+
+ }
+
+ return normalizeAndResolve(identifier);
+ }
+
+ /**
+ * Retrieves the parent paths which should be used while resolving relative
+ * identifiers. By default uses the parent stylesheet location and a
+ * possible absolute path in the identifier.
+ *
+ * @param parentStylesheet
+ * The parent stylesheet or null if there is no parent
+ * @param identifier
+ * The identifier to be resolved
+ * @return a list of paths in which to look for the relative import
+ */
+ protected List<String> getPotentialParentPaths(
+ ScssStylesheet parentStylesheet, String identifier) {
+ List<String> potentialParents = new ArrayList<String>();
+ if (parentStylesheet != null) {
+ potentialParents.add(extractFullPath(
+ parentStylesheet.getDirectory(), identifier));
+ }
+
+ // Identifier can be a full path so extract the path part also as a
+ // potential parent
+ if (identifier.contains("/")) {
+ potentialParents.add(extractFullPath("", identifier));
+ }
+
+ return potentialParents;
+
+ }
+
+ /**
+ * Extracts the full path from the path combined with the identifier
+ *
+ * @param path
+ * The base path
+ * @param identifier
+ * The identifier which may contain a path part, separated by "/"
+ * from the real identifier
+ * @return a normalized version of the path where identifier does not
+ * contain any directory information
+ */
+ protected String extractFullPath(String path, String identifier) {
+ int lastSlashPosition = identifier.lastIndexOf("/");
+ if (lastSlashPosition == -1) {
+ return path;
+ }
+ String identifierPath = identifier.substring(0, lastSlashPosition);
+ if ("".equals(path)) {
+ return identifierPath;
+ } else {
+ return path + "/" + identifierPath;
+ }
+ }
+
+ /**
+ * Resolves the normalized version of the given identifier
+ *
+ * @param identifier
+ * The identifier to resolve
+ * @return An input source if the resolver found one or null otherwise
+ */
+ protected InputSource normalizeAndResolve(String identifier) {
+ String normalized = normalize(identifier);
+ return resolveNormalized(normalized);
+ }
+
+ /**
+ * Resolves the identifier after it has been normalized using
+ * {@link #normalize(String)}.
+ *
+ * @param identifier
+ * The normalized identifier
+ * @return an InputSource if the resolver found a source or null otherwise
+ */
+ protected abstract InputSource resolveNormalized(String identifier);
+
+ /**
+ * Normalizes "." and ".." from the path string where parent path segments
+ * can be removed. Preserve leading "..". Also ensure / is used instead of \
+ * in all places.
+ *
+ * @param path
+ * A relative or absolute file path
+ * @return The normalized path
+ */
+ protected String normalize(String path) {
+
+ // Ensure only "/" is used, also in Windows
+ path = path.replace(File.separatorChar, '/');
+
+ // Split into segments
+ String[] segments = path.split("/");
+ Stack<String> result = new Stack<String>();
+
+ // Replace '.' and '..' segments
+ for (int i = 0; i < segments.length; i++) {
+ if (segments[i].equals(".")) {
+ // Segments marked '.' are ignored
+
+ } else if (segments[i].equals("..") && !result.isEmpty()
+ && !result.lastElement().equals("..")) {
+ // If segment is ".." then remove the previous iff the previous
+ // element is not a ".." and the result stack is not empty
+ result.pop();
+ } else {
+ // Other segments are just added to the stack
+ result.push(segments[i]);
+ }
+ }
+
+ // Reconstruct path
+ StringBuilder pathBuilder = new StringBuilder();
+ for (int i = 0; i < result.size(); i++) {
+ if (i > 0) {
+ pathBuilder.append("/");
+ }
+ pathBuilder.append(result.get(i));
+ }
+ return pathBuilder.toString();
+ }
+
+}
diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java
index 8711a0a3e9..755073bc4c 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java
@@ -15,31 +15,19 @@
*/
package com.vaadin.sass.internal.resolver;
-import java.io.File;
import java.io.InputStream;
import org.w3c.css.sac.InputSource;
-public class ClassloaderResolver implements ScssStylesheetResolver {
+public class ClassloaderResolver extends AbstractResolver {
@Override
- public InputSource resolve(String identifier) {
- // identifier should not have .scss, fileName should
- String ext = ".scss";
- if (identifier.endsWith(".css")) {
- ext = ".css";
- }
+ public InputSource resolveNormalized(String identifier) {
String fileName = identifier;
- if (identifier.endsWith(ext)) {
- identifier = identifier.substring(0,
- identifier.length() - ext.length());
- } else {
- fileName = fileName + ext;
+ if (!fileName.endsWith(".css")) {
+ fileName += ".scss";
}
- // Ensure only "/" is used, also in Windows
- fileName = fileName.replace(File.separatorChar, '/');
-
// Filename should be a relative path starting with VAADIN/...
int vaadinIdx = fileName.lastIndexOf("VAADIN/");
if (vaadinIdx > -1) {
diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java
index 9bb1969ab1..786d0875da 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java
@@ -18,24 +18,46 @@ package com.vaadin.sass.internal.resolver;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
+import java.util.List;
import org.w3c.css.sac.InputSource;
-public class FilesystemResolver implements ScssStylesheetResolver {
+import com.vaadin.sass.internal.ScssStylesheet;
+public class FilesystemResolver extends AbstractResolver {
+
+ private String[] customPaths = null;
+
+ public FilesystemResolver(String... customPaths) {
+ this.customPaths = customPaths;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.sass.internal.resolver.AbstractResolver#getPotentialPaths(
+ * com.vaadin.sass.internal.ScssStylesheet, java.lang.String)
+ */
@Override
- public InputSource resolve(String identifier) {
- // identifier should not have .scss, fileName should
- String ext = ".scss";
- if (identifier.endsWith(".css")) {
- ext = ".css";
+ protected List<String> getPotentialParentPaths(
+ ScssStylesheet parentStyleSheet, String identifier) {
+ List<String> potentialPaths = super.getPotentialParentPaths(
+ parentStyleSheet, identifier);
+ if (customPaths != null) {
+ for (String path : customPaths) {
+ potentialPaths.add(extractFullPath(path, identifier));
+ }
}
+
+ return potentialPaths;
+ }
+
+ @Override
+ public InputSource resolveNormalized(String identifier) {
String fileName = identifier;
- if (identifier.endsWith(ext)) {
- identifier = identifier.substring(0,
- identifier.length() - ext.length());
- } else {
- fileName = fileName + ext;
+ if (!fileName.endsWith(".css")) {
+ fileName += ".scss";
}
try {
diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java
index 45f10836a3..64b3d10d88 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java
@@ -17,6 +17,8 @@ package com.vaadin.sass.internal.resolver;
import org.w3c.css.sac.InputSource;
+import com.vaadin.sass.internal.ScssStylesheet;
+
public interface ScssStylesheetResolver {
/**
* Called with the "identifier" of a stylesheet that the resolver should try
@@ -26,9 +28,12 @@ public interface ScssStylesheetResolver {
* stylesheet was found, e.g "runo.scss" might result in a URI like
* "VAADIN/themes/runo/runo.scss".
*
+ * @param parentStylesheet
+ * The parent style sheet
* @param identifier
* used fo find stylesheet
* @return InputSource for stylesheet (with URI set) or null if not found
*/
- public InputSource resolve(String identifier);
+ public InputSource resolve(ScssStylesheet parentStylesheet,
+ String identifier);
} \ No newline at end of file
diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java
deleted file mode 100644
index fec16a54c8..0000000000
--- a/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2000-2013 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.vaadin.sass.internal.resolver;
-
-import java.io.File;
-import java.util.Stack;
-
-import org.w3c.css.sac.InputSource;
-
-public class VaadinResolver implements ScssStylesheetResolver {
-
- @Override
- public InputSource resolve(String identifier) {
-
- // Remove extra "." and ".."
- identifier = normalize(identifier);
-
- InputSource source = null;
-
- // Can we find the scss from the file system?
- ScssStylesheetResolver resolver = new FilesystemResolver();
- source = resolver.resolve(identifier);
-
- if (source == null) {
- // How about the classpath?
- resolver = new ClassloaderResolver();
- source = resolver.resolve(identifier);
- }
-
- return source;
- }
-
- /**
- * Normalizes "." and ".." from the path string where parent path segments
- * can be removed. Preserve leading "..".
- *
- * @param path
- * A relative or absolute file path
- * @return The normalized path
- */
- private static String normalize(String path) {
-
- // Ensure only "/" is used, also in Windows
- path = path.replace(File.separatorChar, '/');
-
- // Split into segments
- String[] segments = path.split("/");
- Stack<String> result = new Stack<String>();
-
- // Replace '.' and '..' segments
- for (int i = 0; i < segments.length; i++) {
- if (segments[i].equals(".")) {
- // Segments marked '.' are ignored
-
- } else if (segments[i].equals("..") && !result.isEmpty()
- && !result.lastElement().equals("..")) {
- // If segment is ".." then remove the previous iff the previous
- // element is not a ".." and the result stack is not empty
- result.pop();
- } else {
- // Other segments are just added to the stack
- result.push(segments[i]);
- }
- }
-
- // Reconstruct path
- StringBuilder pathBuilder = new StringBuilder();
- for (int i = 0; i < result.size(); i++) {
- if (i > 0) {
- pathBuilder.append("/");
- }
- pathBuilder.append(result.get(i));
- }
- return pathBuilder.toString();
- }
-
-}
diff --git a/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java b/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java
index cb9896967a..e52767bb5a 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java
@@ -16,7 +16,6 @@
package com.vaadin.sass.internal.visitor;
-import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
@@ -57,25 +56,14 @@ public class ImportNodeHandler {
ImportNode importNode = (ImportNode) n;
if (!importNode.isPureCssImport()) {
try {
- StringBuilder filePathBuilder = new StringBuilder(
- styleSheet.getFileName());
- filePathBuilder.append(File.separatorChar).append(
- importNode.getUri());
- if (!filePathBuilder.toString().endsWith(".scss")) {
- filePathBuilder.append(".scss");
- }
-
// set parent's charset to imported node.
ScssStylesheet imported = ScssStylesheet.get(
- filePathBuilder.toString(),
- styleSheet.getCharset());
- if (imported == null) {
- imported = ScssStylesheet.get(importNode.getUri());
- }
+ importNode.getUri(), styleSheet);
if (imported == null) {
- throw new FileNotFoundException(importNode.getUri()
- + " (parent: "
- + ScssStylesheet.get().getFileName() + ")");
+ throw new FileNotFoundException("Import '"
+ + importNode.getUri() + "' in '"
+ + styleSheet.getFileName()
+ + "' could not be found");
}
traverse(imported);
diff --git a/theme-compiler/tests/resources/css/compass-import.css b/theme-compiler/tests/resources/css/compass-import.css
new file mode 100644
index 0000000000..e3d4b5fcca
--- /dev/null
+++ b/theme-compiler/tests/resources/css/compass-import.css
@@ -0,0 +1,49 @@
+.content-navigation {
+ border-color: #3bbfce;
+ color: #0000ff;
+}
+
+.border {
+ padding: 8px;
+ margin: 8px;
+ border-color: #3bbfce;
+}
+
+.body {
+ background-image: url(compass/folder-test2/bg.png);
+ background: transparent url(compass/folder-test2/img/loading-indicator.gif);
+ background-image: url(http://abc/bg.png);
+ background-image: url(/abc/bg.png);
+}
+
+.base {
+ color: red;
+}
+
+.text {
+ font-weight: bold;
+}
+
+.footer {
+ border: 2px solid black;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+}
+
+.banner {
+ border: 1px solid black;
+ font-color: red;
+}
+
+.interpolation-test {
+ font-size: 14px;
+}
+
+.header {
+ width: 100%;
+}
+
+.badError {
+ border-width: 3px;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test/compass-import.scss b/theme-compiler/tests/resources/scss/compass-test/compass-import.scss
new file mode 100644
index 0000000000..36d041b33c
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test/compass-import.scss
@@ -0,0 +1,4 @@
+@import "compass";
+.badError {
+ border-width: 3px;
+}
diff --git a/theme-compiler/tests/resources/scss/compass-test2/_compass.scss b/theme-compiler/tests/resources/scss/compass-test2/_compass.scss
new file mode 100644
index 0000000000..9b741c0f03
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/_compass.scss
@@ -0,0 +1,3 @@
+@import "compass/utilities";
+@import "compass/typography";
+@import "compass/css3";
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass-import2.scss b/theme-compiler/tests/resources/scss/compass-test2/compass-import2.scss
new file mode 100644
index 0000000000..36d041b33c
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass-import2.scss
@@ -0,0 +1,4 @@
+@import "compass";
+.badError {
+ border-width: 3px;
+}
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/_css3.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/_css3.scss
new file mode 100644
index 0000000000..42163ba193
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/_css3.scss
@@ -0,0 +1,3 @@
+@import "css3/border-radius";
+@import "css3/inline-block";
+@import "css3/opacity";
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/_typography.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/_typography.scss
new file mode 100644
index 0000000000..a65c1ff292
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/_typography.scss
@@ -0,0 +1,3 @@
+@import "typography/links";
+@import "typography/lists";
+@import "typography/text";
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/_utilities.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/_utilities.scss
new file mode 100644
index 0000000000..644ad3368b
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/_utilities.scss
@@ -0,0 +1,3 @@
+@import "utilities/color";
+@import "utilities/general";
+@import "utilities/sprites";
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_border-radius.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_border-radius.scss
new file mode 100644
index 0000000000..752003104b
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_border-radius.scss
@@ -0,0 +1,4 @@
+.banner {
+ border: 1px solid black;
+ font-color: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_inline-block.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_inline-block.scss
new file mode 100644
index 0000000000..3fefab83b2
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_inline-block.scss
@@ -0,0 +1,3 @@
+.interpolation-test {
+ font-size: 14px;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_opacity.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_opacity.scss
new file mode 100644
index 0000000000..f6bf34fe24
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/css3/_opacity.scss
@@ -0,0 +1,3 @@
+.header {
+ width: 100%;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_links.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_links.scss
new file mode 100644
index 0000000000..bc7318558e
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_links.scss
@@ -0,0 +1,3 @@
+.base {
+ color: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_lists.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_lists.scss
new file mode 100644
index 0000000000..af174b7095
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_lists.scss
@@ -0,0 +1,3 @@
+.text {
+ font-weight: bold;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_text.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_text.scss
new file mode 100644
index 0000000000..8239527f7b
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/typography/_text.scss
@@ -0,0 +1,6 @@
+.footer {
+ border: 2px solid black;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_color.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_color.scss
new file mode 100644
index 0000000000..ea1b7a55f0
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_color.scss
@@ -0,0 +1,4 @@
+.content-navigation {
+ border-color: #3bbfce;
+ color: #0000ff;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_general.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_general.scss
new file mode 100644
index 0000000000..0c58c6433d
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_general.scss
@@ -0,0 +1,5 @@
+.border {
+ padding: 8px;
+ margin: 8px;
+ border-color: #3bbfce;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_sprites.scss b/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_sprites.scss
new file mode 100644
index 0000000000..28960f89fc
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/compass/utilities/_sprites.scss
@@ -0,0 +1,6 @@
+.body {
+ background-image: url(../folder-test2/bg.png);
+ background: transparent url(../folder-test2/img/loading-indicator.gif);
+ background-image: url(http://abc/bg.png);
+ background-image: url(/abc/bg.png);
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/scss/compass-test2/license-readme.txt b/theme-compiler/tests/resources/scss/compass-test2/license-readme.txt
new file mode 100644
index 0000000000..90ba808179
--- /dev/null
+++ b/theme-compiler/tests/resources/scss/compass-test2/license-readme.txt
@@ -0,0 +1,26 @@
+The design here is to use the stylesheets located at:
+https://github com/chriseppstein/compass/tree/stable/frameworks/compass/stylesheets
+
+and update the VAADIN code to be able to read them in such that an existing JRuby implementation can be replaced with VAADIN without any changes to one's *.scss and *.css files.
+
+The current short snippets of SCSS that are included here only for testing Compass compatibility might not qualify as significant or substantial parts, but in any case Compass is being mentioned for related tests pointing to the original implementation. These small portions of Compass are copied and modified for the testing of compatibility only.
+
+The license for Compass mentioned here:
+https://github.com/chriseppstein/compass/blob/stable/LICENSE.markdown
+
+is as follows:
+
+
+
+
+Copyright (c) 2009 Christopher M. Eppstein
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. No attribution is required by products that make use of this software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization.
+
+Contributors to this project agree to grant all rights to the copyright holder of the primary product. Attribution is maintained in the source control history of the product.
diff --git a/theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java b/theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java
index 59b49888c2..0183142747 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java
@@ -40,16 +40,26 @@ import java.lang.reflect.Method;
import org.junit.Assert;
import org.junit.Test;
-import com.vaadin.sass.internal.resolver.VaadinResolver;
+import com.vaadin.sass.internal.resolver.AbstractResolver;
+import com.vaadin.sass.internal.resolver.ClassloaderResolver;
+import com.vaadin.sass.internal.resolver.FilesystemResolver;
public class VaadinResolverTest {
@Test
- public void testPathNormalization() throws Exception {
+ public void testFilesystemResolverPathNormalization() throws Exception {
+ testPathNormalization(new FilesystemResolver());
+ }
+
+ @Test
+ public void testClassloaderResolverPathNormalization() throws Exception {
+ testPathNormalization(new ClassloaderResolver());
+ }
- VaadinResolver resolver = new VaadinResolver();
+ public void testPathNormalization(AbstractResolver resolver)
+ throws Exception {
- Method normalizeMethod = VaadinResolver.class.getDeclaredMethod(
+ Method normalizeMethod = AbstractResolver.class.getDeclaredMethod(
"normalize", String.class);
normalizeMethod.setAccessible(true);
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/CompassImports.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/CompassImports.java
new file mode 100644
index 0000000000..1e3eb09f0c
--- /dev/null
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/CompassImports.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.sass.testcases.scss;
+
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.w3c.css.sac.CSSException;
+
+import com.vaadin.sass.AbstractTestBase;
+import com.vaadin.sass.internal.ScssStylesheet;
+import com.vaadin.sass.internal.handler.SCSSDocumentHandler;
+import com.vaadin.sass.internal.handler.SCSSDocumentHandlerImpl;
+import com.vaadin.sass.internal.parser.Parser;
+import com.vaadin.sass.internal.resolver.FilesystemResolver;
+import com.vaadin.sass.internal.tree.ImportNode;
+
+public class CompassImports extends AbstractTestBase {
+
+ String scssOtherDirectory = "/scss/compass-test/compass-import.scss";
+ String scssSameDirectory = "/scss/compass-test2/compass-import2.scss";
+ String css = "/css/compass-import.css";
+
+ String compassPath = "/scss/compass-test2";
+
+ @Test
+ public void testParser() throws CSSException, IOException {
+ Parser parser = new Parser();
+ SCSSDocumentHandler handler = new SCSSDocumentHandlerImpl();
+ parser.setDocumentHandler(handler);
+ parser.parseStyleSheet(getClass().getResource(scssOtherDirectory)
+ .getPath());
+ ScssStylesheet root = handler.getStyleSheet();
+ ImportNode importVariableNode = (ImportNode) root.getChildren().get(0);
+ Assert.assertEquals("compass", importVariableNode.getUri());
+ Assert.assertFalse(importVariableNode.isPureCssImport());
+ }
+
+ @Test
+ public void testCompiler() throws Exception {
+ testCompiler(scssSameDirectory, css, null);
+ }
+
+ @Test
+ public void testCompilerWithCustomPath() throws Exception {
+ File rootPath = new File(getClass().getResource(compassPath).toURI());
+
+ testCompiler(scssOtherDirectory, css, rootPath.getPath());
+ }
+
+ public void testCompiler(String scss, String css, String additionalPath)
+ throws Exception {
+ comparisonCss = getFileContent(css);
+ ScssStylesheet sheet = getStyleSheet(scss);
+ Assert.assertNotNull(sheet);
+ sheet.addResolver(new FilesystemResolver(additionalPath));
+
+ sheet.compile();
+ parsedScss = sheet.toString();
+ Assert.assertEquals("Original CSS and parsed CSS do not match",
+ comparisonCss, parsedScss);
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.html b/uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.html
new file mode 100644
index 0000000000..33fc46f060
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.html
@@ -0,0 +1,92 @@
+<?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.application.DetachOldUIOnReload?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td>
+ <td>This is UI 0</td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.application.DetachOldUIOnReload</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::PID_SLog_row_0</td>
+ <td>1. UI 0 has been detached</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td>
+ <td>This is UI 1</td>
+</tr>
+<tr>
+ <td>clickAndWait</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::PID_SLog_row_0</td>
+ <td>2. UI 1 has been detached</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td>
+ <td>This is UI 2</td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.application.DetachOldUIOnReload</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::PID_SLog_row_0</td>
+ <td>3. UI 2 has been detached</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestsapplicationDetachOldUIOnReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td>
+ <td>This is UI 3</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.java b/uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.java
new file mode 100644
index 0000000000..154c84b4f5
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/application/DetachOldUIOnReload.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.tests.application;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Label;
+
+public class DetachOldUIOnReload extends AbstractTestUIWithLog {
+ private static final String PERSISTENT_MESSAGES_ATTRIBUTE = DetachOldUIOnReload.class
+ .getName() + ".sessionMessages";
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ addComponent(new Label("This is UI " + getUIId()));
+ addComponent(new Button("Reload page", new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ getPage().reload();
+ }
+ }));
+ addComponent(new Button("Read log messages from session",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ for (String message : getSessionMessages(false)) {
+ log(message);
+ }
+ }
+ }));
+ }
+
+ private List<String> getSessionMessages(boolean storeIfNeeded) {
+ List<String> messages = (List<String>) getSession().getAttribute(
+ PERSISTENT_MESSAGES_ATTRIBUTE);
+ if (messages == null) {
+ messages = new ArrayList<String>();
+ if (storeIfNeeded) {
+ getSession().setAttribute(PERSISTENT_MESSAGES_ATTRIBUTE,
+ messages);
+ }
+ }
+ return messages;
+ }
+
+ private void logToSession(String message) {
+ getSessionMessages(true).add(message);
+ }
+
+ @Override
+ public void detach() {
+ super.detach();
+ logToSession("UI " + getUIId() + " has been detached");
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Tests that the previous UI gets cleaned immediately when refreshing.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return Integer.valueOf(10338);
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.html b/uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.html
new file mode 100644
index 0000000000..f366054f45
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.html
@@ -0,0 +1,36 @@
+<?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/run/com.vaadin.tests.application.RefreshStatePreserveTitle?restartApplication" />
+<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.application.RefreshStatePreserveTitle?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertTitle</td>
+ <td>TEST</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.application.RefreshStatePreserveTitle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertTitle</td>
+ <td>TEST</td>
+ <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.java b/uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.java
new file mode 100644
index 0000000000..88b3a9b9f4
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/application/RefreshStatePreserveTitle.java
@@ -0,0 +1,30 @@
+package com.vaadin.tests.application;
+
+import com.vaadin.annotations.PreserveOnRefresh;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.Label;
+
+@PreserveOnRefresh
+public class RefreshStatePreserveTitle extends AbstractTestUI {
+
+ private Log log = new Log(5);
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ getPage().setTitle("TEST");
+ addComponent(new Label(
+ "Refresh the page and observe that window title 'TEST' is lost."));
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Refreshing the browser window should preserve the window title";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return Integer.valueOf(11054);
+ }
+} \ No newline at end of file
diff --git a/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.html b/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.html
index f20967c8de..e7d437eeca 100644
--- a/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.html
+++ b/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.html
@@ -28,7 +28,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestsapplicationTerminalErrorNotification::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestsapplicationTerminalErrorNotification::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>Got an exception: You asked for it</td>
</tr>
</tbody></table>
diff --git a/uitest/src/com/vaadin/tests/application/VaadinSessionAttribute.html b/uitest/src/com/vaadin/tests/application/VaadinSessionAttribute.html
index e03027f308..150ab3ff3d 100644
--- a/uitest/src/com/vaadin/tests/application/VaadinSessionAttribute.html
+++ b/uitest/src/com/vaadin/tests/application/VaadinSessionAttribute.html
@@ -23,7 +23,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>//body/div[2]</td>
+ <td>//body/div[2]//h1</td>
<td>42 &amp; 84</td>
</tr>
</tbody></table>
diff --git a/uitest/src/com/vaadin/tests/components/ErrorMessages.html b/uitest/src/com/vaadin/tests/components/ErrorMessages.html
index 5379ff59e0..2b7032ea79 100644
--- a/uitest/src/com/vaadin/tests/components/ErrorMessages.html
+++ b/uitest/src/com/vaadin/tests/components/ErrorMessages.html
@@ -3,17 +3,17 @@
<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://artur-laptop.office.itmill.com:8888/" />
-<title>New Test</title>
+<link rel="selenium.base" href="http://localhost:8888/" />
+<title>ErrorMessages</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
-<tr><td rowspan="1" colspan="3">New Test</td></tr>
+<tr><td rowspan="1" colspan="3">ErrorMessages</td></tr>
</thead><tbody>
<tr>
<td>open</td>
- <td>/run/com.vaadin.tests.components.ErrorMessages</td>
+ <td>/run/com.vaadin.tests.components.ErrorMessages?restartApplication</td>
<td></td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java b/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java
index 1208b8be3b..cc75f87a71 100644
--- a/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java
+++ b/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java
@@ -22,6 +22,7 @@ public class ButtonsWaiAria extends ComponentTestCase<Button> {
l = createButton("Icon Button, empty alt", nat);
l.setIcon(ICON_16_USER_PNG_CACHEABLE);
+ l.setDescription("Empty alt text");
addTestComponent(l);
l = createButton("Icon Button with alt", nat);
diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html
index fd51a0daad..6add1deba5 100644
--- a/uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html
+++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html
@@ -313,7 +313,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>8,9</td>
</tr>
<!--Edit previously created events and change properties-->
@@ -438,7 +438,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>11,8</td>
</tr>
<tr>
@@ -558,7 +558,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>7,8</td>
</tr>
<tr>
@@ -583,7 +583,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>12,10</td>
</tr>
<!--Go to monthly view and assert inserted events-->
diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html
index 03ec163e40..3c91c8b24f 100644
--- a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html
+++ b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropWrapperTooltips.html
@@ -18,7 +18,7 @@
</tr>
<tr>
<td>showTooltip</td>
- <td>vaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper3</td>
+ <td>vaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper3/VLabel[0]</td>
<td>0,0</td>
</tr>
<tr>
@@ -26,9 +26,10 @@
<td></td>
<td>tooltip-initial</td>
</tr>
+<!--Drag Block 4 between Block 1 and Block 2-->
<tr>
<td>drag</td>
- <td>vaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper4</td>
+ <td>vaadin=runcomvaadintestscomponentsdraganddropwrapperDragAndDropWrapperTooltips::PID_Swrapper4/VLabel[0]</td>
<td>30,41</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html
index 2dcd1b5071..ae81cfe61c 100644
--- a/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html
+++ b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html
@@ -23,7 +23,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>41, 22</td>
</tr>
<tr>
@@ -33,7 +33,7 @@
</tr>
<tr>
<td>waitForElementNotPresent</td>
- <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td></td>
</tr>
<tr>
@@ -43,7 +43,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>0, 0</td>
</tr>
</tbody></table>
diff --git a/uitest/src/com/vaadin/tests/components/form/FormTooltips.html b/uitest/src/com/vaadin/tests/components/form/FormTooltips.html
index ee693fd4be..5e412c3aad 100644
--- a/uitest/src/com/vaadin/tests/components/form/FormTooltips.html
+++ b/uitest/src/com/vaadin/tests/components/form/FormTooltips.html
@@ -3,7 +3,7 @@
<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:8070" />
+<link rel="selenium.base" href="http://localhost:8888" />
<title>FormTooltips</title>
</head>
<body>
@@ -38,10 +38,15 @@
<td></td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
+ <td>-1000</td>
+</tr>
<!--first name caption tooltip-->
<tr>
<td>showTooltip</td>
@@ -64,10 +69,15 @@
<td></td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
+ <td>-1000</td>
+</tr>
<!--Form should not have a description tooltip-->
<tr>
<td>showTooltip</td>
@@ -80,9 +90,9 @@
<td></td>
</tr>
<tr>
- <td>assertElementNotPresent</td>
+ <td>assertElementPositionLeft</td>
<td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]</td>
- <td></td>
+ <td>-1000</td>
</tr>
<!--Form error message should not have a tooltip-->
<tr>
@@ -96,9 +106,9 @@
<td></td>
</tr>
<tr>
- <td>assertElementNotPresent</td>
+ <td>assertElementPositionLeft</td>
<td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]</td>
- <td></td>
+ <td>-1000</td>
</tr>
<!--last name should have no tooltip-->
<tr>
@@ -112,9 +122,9 @@
<td></td>
</tr>
<tr>
- <td>assertElementNotPresent</td>
+ <td>assertElementPositionLeft</td>
<td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
- <td></td>
+ <td>-1000</td>
</tr>
<!--last name caption should have no tooltip-->
<tr>
@@ -128,9 +138,9 @@
<td></td>
</tr>
<tr>
- <td>assertElementNotPresent</td>
+ <td>assertElementPositionLeft</td>
<td>vaadin=runcomvaadintestscomponentsformFormTooltips::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
- <td></td>
+ <td>-1000</td>
</tr>
</tbody></table>
diff --git a/uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndText.java b/uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndText.java
new file mode 100644
index 0000000000..fdeed316ba
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndText.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.nativebutton;
+
+import com.vaadin.server.ThemeResource;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.NativeButton;
+
+public class NativeButtonIconAndText extends AbstractTestUI implements
+ ClickListener {
+
+ static final String UPDATED_ALTERNATE_TEXT = "Now has alternate text";
+ static final String INITIAL_ALTERNATE_TEXT = "Initial alternate text";
+ static final String BUTTON_TEXT = "buttonText";
+ static final String BUTTON_TEXT_ICON = "buttonTextIcon";
+ static final String BUTTON_TEXT_ICON_ALT = "buttonTextIconAlt";
+ static final String NATIVE_BUTTON_TEXT = "nativeButtonText";
+ static final String NATIVE_BUTTON_TEXT_ICON = "nativeButtonTextIcon";
+ static final String NATIVE_BUTTON_TEXT_ICON_ALT = "nativeButtonTextIconAlt";
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ Button buttonText = new Button("Only text");
+
+ Button buttonTextIcon = new Button("Text icon");
+ buttonTextIcon.setIcon(new ThemeResource("../runo/icons/64/ok.png"));
+
+ Button buttonTextIconAlt = new Button("Text icon alt");
+ buttonTextIconAlt.setIcon(new ThemeResource(
+ "../runo/icons/64/cancel.png"));
+ buttonTextIconAlt.setIconAlternateText(INITIAL_ALTERNATE_TEXT);
+
+ buttonText.addClickListener(this);
+ buttonTextIcon.addClickListener(this);
+ buttonTextIconAlt.addClickListener(this);
+
+ buttonText.setId(BUTTON_TEXT);
+ buttonTextIcon.setId(BUTTON_TEXT_ICON);
+ buttonTextIconAlt.setId(BUTTON_TEXT_ICON_ALT);
+
+ addComponent(buttonText);
+ addComponent(buttonTextIcon);
+ addComponent(buttonTextIconAlt);
+
+ NativeButton nativeButtonText = new NativeButton("Only text");
+
+ NativeButton nativeButtonTextIcon = new NativeButton("Text icon");
+ nativeButtonTextIcon.setIcon(new ThemeResource(
+ "../runo/icons/64/ok.png"));
+
+ NativeButton nativeButtonTextIconAlt = new NativeButton("Text icon alt");
+ nativeButtonTextIconAlt.setIcon(new ThemeResource(
+ "../runo/icons/64/cancel.png"));
+ nativeButtonTextIconAlt.setIconAlternateText(INITIAL_ALTERNATE_TEXT);
+
+ nativeButtonText.addClickListener(this);
+ nativeButtonTextIcon.addClickListener(this);
+ nativeButtonTextIconAlt.addClickListener(this);
+
+ nativeButtonText.setId(NATIVE_BUTTON_TEXT);
+ nativeButtonTextIcon.setId(NATIVE_BUTTON_TEXT_ICON);
+ nativeButtonTextIconAlt.setId(NATIVE_BUTTON_TEXT_ICON_ALT);
+
+ addComponent(nativeButtonText);
+ addComponent(nativeButtonTextIcon);
+ addComponent(nativeButtonTextIconAlt);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription()
+ */
+ @Override
+ protected String getTestDescription() {
+ return "Click the buttons to toggle icon alternate text";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber()
+ */
+ @Override
+ protected Integer getTicketNumber() {
+ return 12780;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Button.ClickListener#buttonClick(com.vaadin.ui.Button.
+ * ClickEvent)
+ */
+ @Override
+ public void buttonClick(ClickEvent event) {
+ Button b = event.getButton();
+ String was = b.getIconAlternateText();
+ if (was == null || was.isEmpty()) {
+ b.setIconAlternateText(UPDATED_ALTERNATE_TEXT);
+ } else {
+ b.setIconAlternateText(null);
+ }
+
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndTextTest.java b/uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndTextTest.java
new file mode 100644
index 0000000000..2cb294de77
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/nativebutton/NativeButtonIconAndTextTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.nativebutton;
+
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.BUTTON_TEXT;
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.BUTTON_TEXT_ICON;
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.BUTTON_TEXT_ICON_ALT;
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.INITIAL_ALTERNATE_TEXT;
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.NATIVE_BUTTON_TEXT;
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.NATIVE_BUTTON_TEXT_ICON;
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.NATIVE_BUTTON_TEXT_ICON_ALT;
+import static com.vaadin.tests.components.nativebutton.NativeButtonIconAndText.UPDATED_ALTERNATE_TEXT;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+public class NativeButtonIconAndTextTest extends MultiBrowserTest {
+
+ @Test
+ public void testNativeButtonIconAltText() {
+ openTestURL();
+ assertAltText(BUTTON_TEXT, "");
+ assertAltText(BUTTON_TEXT_ICON, "");
+ assertAltText(BUTTON_TEXT_ICON_ALT, INITIAL_ALTERNATE_TEXT);
+ assertAltText(NATIVE_BUTTON_TEXT, "");
+ assertAltText(NATIVE_BUTTON_TEXT_ICON, "");
+ assertAltText(NATIVE_BUTTON_TEXT_ICON_ALT, INITIAL_ALTERNATE_TEXT);
+
+ clickElements(BUTTON_TEXT, BUTTON_TEXT_ICON, BUTTON_TEXT_ICON_ALT,
+ NATIVE_BUTTON_TEXT, NATIVE_BUTTON_TEXT_ICON,
+ NATIVE_BUTTON_TEXT_ICON_ALT);
+
+ // Button without icon - should not get alt text
+ assertAltText(BUTTON_TEXT, "");
+ assertAltText(BUTTON_TEXT_ICON, UPDATED_ALTERNATE_TEXT);
+ assertAltText(BUTTON_TEXT_ICON_ALT, "");
+ // Button without icon - should not get alt text
+ assertAltText(NATIVE_BUTTON_TEXT, "");
+ assertAltText(NATIVE_BUTTON_TEXT_ICON, UPDATED_ALTERNATE_TEXT);
+ assertAltText(NATIVE_BUTTON_TEXT_ICON_ALT, "");
+
+ }
+
+ private void clickElements(String... ids) {
+ for (String id : ids) {
+ vaadinElementById(id).click();
+ }
+ }
+
+ /**
+ * If the button identified by 'buttonId' has an icon, asserts that the
+ * alternate text of the icon matches 'expected'. "" and null are considered
+ * equivalent.
+ *
+ * @param buttonId
+ * the id of the button who possibly contains an icon
+ * @param expected
+ * the expected alternate text, cannot be null
+ */
+ private void assertAltText(String buttonId, String expected) {
+ WebElement button = vaadinElementById(buttonId);
+ List<WebElement> imgList = button.findElements(By.xpath(".//img"));
+ if (imgList.isEmpty()) {
+ return;
+ }
+ WebElement img = imgList.get(0);
+ String alt = img.getAttribute("alt");
+ if (alt == null) {
+ alt = "";
+ }
+
+ Assert.assertEquals(expected, alt);
+
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/components/notification/CloseErrorNotificationWithEscape.html b/uitest/src/com/vaadin/tests/components/notification/CloseErrorNotificationWithEscape.html
index 1ab75e1176..c9d9e186bc 100644
--- a/uitest/src/com/vaadin/tests/components/notification/CloseErrorNotificationWithEscape.html
+++ b/uitest/src/com/vaadin/tests/components/notification/CloseErrorNotificationWithEscape.html
@@ -28,12 +28,12 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentsnotificationNotifications::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotifications::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>Hello world</td>
</tr>
<tr>
<td>keyDown</td>
- <td>vaadin=runcomvaadintestscomponentsnotificationNotifications::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotifications::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>\27</td>
</tr>
<!-- Fade delay is 400 ms by default - VNotification -->
diff --git a/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.html b/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.html
new file mode 100644
index 0000000000..fb00953e48
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.html
@@ -0,0 +1,107 @@
+<?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>NotificationsWaiAria</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">NotificationsWaiAria</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.components.notification.NotificationsWaiAria?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>enterCharacter</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTextField[0]</td>
+ <td>Prefix:</td>
+</tr>
+<tr>
+ <td>enterCharacter</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VTextField[0]</td>
+ <td>- press ESC to close</td>
+</tr>
+<tr>
+ <td>select</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VNativeSelect[0]/domChild[0]</td>
+ <td>label=ALERT</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[5]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::Root/VNotification[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::Root/VNotification[0]@role</td>
+ <td>alert</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>xpath=//div[@class='v-Notification humanized v-Notification-humanized']//span[@class='v-assistive-device-only'][1]</td>
+ <td>Prefix:</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>xpath=//div[@class='v-Notification humanized v-Notification-humanized']//span[@class='v-assistive-device-only'][2]</td>
+ <td>- press ESC to close</td>
+</tr>
+<tr>
+ <td>closeNotification</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::Root/VNotification[0]</td>
+ <td>0,0</td>
+</tr>
+<tr>
+ <td>select</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VNativeSelect[0]/domChild[0]</td>
+ <td>label=STATUS</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[5]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>xpath=/html/body/div[2]/div@role</td>
+ <td>status</td>
+</tr>
+<tr>
+ <td>closeNotification</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::Root/VNotification[0]</td>
+ <td>0,0</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTextField[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VTextField[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[5]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentsnotificationNotificationsWaiAria::Root/VNotification[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td></td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java b/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java
new file mode 100644
index 0000000000..27af49a397
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java
@@ -0,0 +1,116 @@
+package com.vaadin.tests.components.notification;
+
+import com.vaadin.data.Item;
+import com.vaadin.server.Page;
+import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role;
+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.ComboBox;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.Notification;
+import com.vaadin.ui.Notification.Type;
+import com.vaadin.ui.TextArea;
+import com.vaadin.ui.TextField;
+
+public class NotificationsWaiAria extends TestBase {
+
+ private static final String CAPTION = "CAPTION";
+ private static final String ROLE = "ROLE";
+
+ private TextField prefix;
+ private TextField postfix;
+ private NativeSelect role;
+
+ private TextArea tf;
+ private ComboBox type;
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected void setup() {
+ prefix = new TextField("Prefix", "Info");
+ addComponent(prefix);
+
+ postfix = new TextField("Postfix",
+ " - closes automatically after 10 seconds");
+ addComponent(postfix);
+
+ role = new NativeSelect("Role");
+ role.addItem(Role.ALERT);
+ role.addItem(Role.STATUS);
+ role.setValue(role.getItemIds().iterator().next());
+ addComponent(role);
+
+ tf = new TextArea("Text", "Hello world");
+ tf.setRows(10);
+ addComponent(tf);
+ type = new ComboBox();
+ type.setNullSelectionAllowed(false);
+ type.addContainerProperty(CAPTION, String.class, "");
+
+ type.setItemCaptionPropertyId(CAPTION);
+
+ Item item = type.addItem(Notification.TYPE_HUMANIZED_MESSAGE);
+ item.getItemProperty(CAPTION).setValue("Humanized");
+
+ item = type.addItem(Notification.TYPE_ERROR_MESSAGE);
+ item.getItemProperty(CAPTION).setValue("Error");
+
+ item = type.addItem(Notification.TYPE_WARNING_MESSAGE);
+ item.getItemProperty(CAPTION).setValue("Warning");
+
+ item = type.addItem(Notification.TYPE_TRAY_NOTIFICATION);
+ item.getItemProperty(CAPTION).setValue("Tray");
+
+ item = type.addItem(Notification.Type.ASSISTIVE_NOTIFICATION);
+ item.getItemProperty(CAPTION).setValue("Assistive");
+
+ type.setValue(type.getItemIds().iterator().next());
+ addComponent(type);
+
+ Button showNotification = new Button("Show notification",
+ new SettingHandler());
+ addComponent(showNotification);
+
+ Button showDefaultNotification = new Button("Default notification",
+ new DefaultHandler());
+ addComponent(showDefaultNotification);
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Generic test case for notifications";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private class SettingHandler implements ClickListener {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ Type typeValue = (Type) type.getValue();
+
+ Notification n = new Notification(tf.getValue(), typeValue);
+ n.setHtmlContentAllowed(true);
+ n.setAssistivePrefixForType(typeValue, prefix.getValue());
+ n.setAssistivePostfixForType(typeValue, postfix.getValue());
+ n.setAssistiveRoleForType(typeValue, (Role) role.getValue());
+
+ n.show(Page.getCurrent());
+ }
+ }
+
+ private class DefaultHandler implements ClickListener {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ Notification n = new Notification(tf.getValue(),
+ (Type) type.getValue());
+ n.setHtmlContentAllowed(true);
+ n.show(Page.getCurrent());
+ }
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/components/slider/SliderTooltip.html b/uitest/src/com/vaadin/tests/components/slider/SliderTooltip.html
index 4e8296050f..806e7d1b44 100644
--- a/uitest/src/com/vaadin/tests/components/slider/SliderTooltip.html
+++ b/uitest/src/com/vaadin/tests/components/slider/SliderTooltip.html
@@ -3,13 +3,13 @@
<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>
+<link rel="selenium.base" href="http://localhost:8888/" />
+<title>SliderTooltip</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
-<tr><td rowspan="1" colspan="3">New Test</td></tr>
+<tr><td rowspan="1" colspan="3">SliderTooltip</td></tr>
</thead><tbody>
<tr>
<td>open</td>
@@ -57,9 +57,14 @@
<td>40,16</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runcomvaadintestscomponentssliderSliderTest::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
+ <td>pause</td>
<td></td>
+ <td>1000</td>
+</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runcomvaadintestscomponentssliderSliderTest::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
+ <td>-1000</td>
</tr>
</tbody></table>
diff --git a/uitest/src/com/vaadin/tests/components/table/DoublesInTable.java b/uitest/src/com/vaadin/tests/components/table/DoublesInTable.java
index 355cd43d37..4cf2506eeb 100644
--- a/uitest/src/com/vaadin/tests/components/table/DoublesInTable.java
+++ b/uitest/src/com/vaadin/tests/components/table/DoublesInTable.java
@@ -9,7 +9,7 @@ import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.data.util.converter.Converter;
-import com.vaadin.data.util.converter.StringToNumberConverter;
+import com.vaadin.data.util.converter.StringToDoubleConverter;
import com.vaadin.tests.components.TestBase;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.Country;
@@ -276,7 +276,7 @@ public class DoublesInTable extends TestBase {
});
- t.setConverter("rent", new StringToNumberConverter() {
+ t.setConverter("rent", new StringToDoubleConverter() {
@Override
protected NumberFormat getFormat(Locale locale) {
return NumberFormat.getCurrencyInstance(locale);
diff --git a/uitest/src/com/vaadin/tests/components/table/TableDragColumnFloatingElementStyles.html b/uitest/src/com/vaadin/tests/components/table/TableDragColumnFloatingElementStyles.html
new file mode 100644
index 0000000000..e225091b5f
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/table/TableDragColumnFloatingElementStyles.html
@@ -0,0 +1,124 @@
+<?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>TableDragColumn</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">TableDragColumn</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.components.table.Tables?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>drag</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>51,6</td>
+</tr>
+<tr>
+ <td>mouseMoveAt</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]</td>
+ <td>70,10</td>
+</tr>
+<tr>
+ <td>screenCapture</td>
+ <td></td>
+ <td>1 basic ghost element</td>
+</tr>
+<tr>
+ <td>drop</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>10,10</td>
+</tr>
+<!--Add style name "red-border-1px" to table-->
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_Smenu#item0</td>
+ <td>24,7</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[0]/VMenuBar[0]#item1</td>
+ <td>18,10</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[1]/VMenuBar[0]#item4</td>
+ <td>19,8</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[2]/VMenuBar[0]#item2</td>
+ <td>75,7</td>
+</tr>
+<!-- Drag and drop column 1 to the left of column 4 -->
+<tr>
+ <td>drag</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>51,6</td>
+</tr>
+<tr>
+ <td>mouseMoveAt</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]</td>
+ <td>70,10</td>
+</tr>
+<tr>
+ <td>screenCapture</td>
+ <td></td>
+ <td>2 themed ghost element should have red borders</td>
+</tr>
+<tr>
+ <td>drop</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>10,10</td>
+</tr>
+<!--Add style name "red-border-1px" to table-->
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_Smenu#item0</td>
+ <td>24,7</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[0]/VMenuBar[0]#item1</td>
+ <td>18,10</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[1]/VMenuBar[0]#item4</td>
+ <td>19,8</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[2]/VMenuBar[0]#item3</td>
+ <td>164,10</td>
+</tr>
+<tr>
+ <td>drag</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>51,6</td>
+</tr>
+<tr>
+ <td>mouseMoveAt</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]</td>
+ <td>70,10</td>
+</tr>
+<tr>
+ <td>screenCapture</td>
+ <td></td>
+ <td>3 themed ghost element should have 2px blue borders</td>
+</tr>
+<tr>
+ <td>drop</td>
+ <td>vaadin=runcomvaadintestscomponentstableTables::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>10,10</td>
+</tr>
+</tbody></table>
+</body>
+</html> \ No newline at end of file
diff --git a/uitest/src/com/vaadin/tests/components/table/TableItemDescriptionGeneratorTest.html b/uitest/src/com/vaadin/tests/components/table/TableItemDescriptionGeneratorTest.html
index eb3efc28fd..2df9fb678c 100644
--- a/uitest/src/com/vaadin/tests/components/table/TableItemDescriptionGeneratorTest.html
+++ b/uitest/src/com/vaadin/tests/components/table/TableItemDescriptionGeneratorTest.html
@@ -3,13 +3,13 @@
<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:8888/" />
-<title>New Test</title>
+<link rel="selenium.base" href="http://localhost:8888/" />
+<title>TableItemDescriptionGeneratorTest</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
-<tr><td rowspan="1" colspan="3">New Test</td></tr>
+<tr><td rowspan="1" colspan="3">TableItemDescriptionGeneratorTest</td></tr>
</thead><tbody>
<tr>
<td>open</td>
@@ -39,10 +39,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--Button tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -65,10 +70,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--TextField tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -91,10 +101,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--Cell and row tooltips-->
<tr>
<td>mouseClick</td>
@@ -123,10 +138,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--Button tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -149,10 +169,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--TextField tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -175,10 +200,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--Row and Component tooltips-->
<tr>
<td>mouseClick</td>
@@ -212,10 +242,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--Button tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -238,10 +273,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--TextField tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -264,10 +304,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--Row tooltips-->
<tr>
<td>mouseClick</td>
@@ -296,10 +341,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--Button tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -322,10 +372,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
<!--TextField tooltip-->
<tr>
<td>mouseMoveAt</td>
@@ -348,10 +403,15 @@
<td>22,7</td>
</tr>
<tr>
- <td>waitForElementNotPresent</td>
- <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>pause</td>
+ <td>1000</td>
<td></td>
</tr>
+<tr>
+ <td>assertElementPositionLeft</td>
+ <td>vaadin=runTableItemDescriptionGeneratorTest::Root/VTooltip[0]</td>
+ <td>-1000</td>
+</tr>
</tbody></table>
</body>
diff --git a/uitest/src/com/vaadin/tests/components/table/TableRemovedQuicklySendsInvalidRpcCalls.java b/uitest/src/com/vaadin/tests/components/table/TableRemovedQuicklySendsInvalidRpcCalls.java
new file mode 100644
index 0000000000..6e4b62e4f7
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/table/TableRemovedQuicklySendsInvalidRpcCalls.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.tests.components.table;
+
+import com.vaadin.annotations.Push;
+import com.vaadin.event.ItemClickEvent;
+import com.vaadin.event.ItemClickEvent.ItemClickListener;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Table;
+
+@Push
+public class TableRemovedQuicklySendsInvalidRpcCalls extends AbstractTestUI {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ addComponent(new Button("Blink a table", new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ blinkTable();
+ }
+ }));
+ }
+
+ private void blinkTable() {
+ final Table table = new Table();
+ table.setPageLength(5);
+ table.addContainerProperty(new Object(), String.class, null);
+
+ for (int i = 0; i < 50; i++) {
+ table.addItem(new Object[] { "Row" }, new Object());
+ }
+
+ table.addItemClickListener(new ItemClickListener() {
+ private int i;
+
+ @Override
+ public void itemClick(ItemClickEvent event) {
+ /*
+ * Ignore implementation. This is only an easy way to make the
+ * client-side update table's variables (by furiously clicking
+ * on the table row.
+ *
+ * This way, we get variable changes queued. The push call will
+ * then remove the Table, while the variable changes being still
+ * in the queue, leading to the issue as described in the
+ * ticket.
+ */
+ System.out.println("clicky " + (++i));
+ }
+ });
+
+ System.out.println("adding component");
+ addComponent(table);
+
+ new Thread() {
+ @Override
+ public void run() {
+ getSession().lock();
+ try {
+ Thread.sleep(500);
+ access(new Runnable() {
+ @Override
+ public void run() {
+ System.out.println("removing component");
+ removeComponent(table);
+ }
+ });
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ getSession().unlock();
+ }
+ };
+ }.start();
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Adding and subsequently quickly removing a table "
+ + "should not leave any pending RPC calls waiting "
+ + "in a Timer. Issue can be reproduced by "
+ + "1) pressing the button 2) clicking furiously "
+ + "on a row in the table.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 12337;
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/components/table/TableShouldNotEatValueChanges.html b/uitest/src/com/vaadin/tests/components/table/TableShouldNotEatValueChanges.html
index bab6c8dc16..e24f4ddca4 100644
--- a/uitest/src/com/vaadin/tests/components/table/TableShouldNotEatValueChanges.html
+++ b/uitest/src/com/vaadin/tests/components/table/TableShouldNotEatValueChanges.html
@@ -33,7 +33,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentstableTableShouldNotEatValueChanges::Root/VNotification[0]</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableShouldNotEatValueChanges::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>TF Value on the server:fooo</td>
</tr>
<tr>
@@ -63,7 +63,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentstableTableShouldNotEatValueChanges::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableShouldNotEatValueChanges::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>TF Value on the server:baar</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html
index c2481e6be6..441324bb6a 100644
--- a/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html
+++ b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.html
@@ -3,7 +3,6 @@
<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:9999/" />
<title>TableWithBrokenGeneratorAndContainer</title>
</head>
<body>
@@ -163,7 +162,7 @@
<!--error notification-->
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentstableTableWithBrokenGeneratorAndContainer::Root/VNotification[0]/HTML[0]/domChild[1]</td>
<td>Problem updating table. Please try again later</td>
</tr>
<!--table should be empty-->
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html
index 4b2ad890c3..825988173a 100644
--- a/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html
@@ -33,6 +33,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[1]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 1</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>space</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[1]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 2</td>
</tr>
<tr>
@@ -58,6 +68,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[3]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 2</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>space</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[3]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 5</td>
</tr>
<tr>
@@ -83,6 +103,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[4]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 5</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>space</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[4]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 6</td>
</tr>
<tr>
@@ -143,6 +173,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[8]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 9</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[10]</td>
+ <td>space</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[8]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 12</td>
</tr>
<tr>
@@ -178,6 +218,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 5</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>space</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 1</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java
new file mode 100644
index 0000000000..e394594176
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java
@@ -0,0 +1,86 @@
+package com.vaadin.tests.components.tabsheet;
+
+import java.util.ArrayList;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+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.Layout;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.TabSheet.Tab;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+
+public class TabKeyboardNavigationWaiAria extends AbstractTestUI {
+
+ int index = 1;
+ ArrayList<Component> tabs = new ArrayList<Component>();
+ TabSheet ts = new TabSheet();
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ ts.setWidth("500px");
+ ts.setHeight("500px");
+
+ for (int i = 0; i < 5; ++i) {
+ addTab();
+ }
+
+ Button addTab = new Button("Add a tab", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ addTab();
+ }
+ });
+ Button focus = new Button("Focus tabsheet", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ ts.focus();
+ }
+ });
+
+ addComponent(addTab);
+ addComponent(focus);
+
+ addComponent(ts);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "The tab bar should be focusable and arrow keys should change focus for tabs. Space key selects a focused tab. The del key should close a tab if closable.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 11827;
+ }
+
+ private Tab addTab() {
+ Layout content = new VerticalLayout();
+ tabs.add(content);
+
+ TextField field = new TextField("Tab " + index + " label");
+ content.addComponent(field);
+
+ Tab tab = ts.addTab(content, "Tab " + index, null);
+
+ if (index == 2) {
+ tab.setClosable(true);
+ tab.setDescription("Tab 2 Tooltip");
+ }
+
+ if (index == 4) {
+ tab.setEnabled(false);
+ }
+
+ if (index == 5) {
+ tab.setDefaultFocusComponent(field);
+ }
+
+ index++;
+ return tab;
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.html b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.html
index 425da11af4..6876497a1f 100644
--- a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.html
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.html
@@ -21,6 +21,16 @@
<td></td>
<td></td>
</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentstabsheetTabSheetIcons::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]@alt</td>
+ <td>iconalt1</td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentstabsheetTabSheetIcons::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[0]/domChild[0]@alt</td>
+ <td>iconalt3</td>
+</tr>
</tbody></table>
</body>
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java
index 5d814ec48f..ccdc4ecb38 100644
--- a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java
@@ -46,6 +46,8 @@ public class TabSheetIcons extends TestBase {
for (Component c : tab) {
tabsheet.addTab(c);
+ tabsheet.getTab(c).setIconAltText(
+ "iconalt" + tabsheet.getComponentCount());
}
return tabsheet;
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetTest.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetTest.java
index 6c39cdab73..56836037b7 100644
--- a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetTest.java
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetTest.java
@@ -26,7 +26,7 @@ public class TabSheetTest<T extends TabSheet> extends
@Override
public void execute(T c, Integer value, Object data) {
- c.getTab(value).setIcon((Resource) data);
+ c.getTab(value).setIcon((Resource) data, "tabicon");
}
};
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.html b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.html
new file mode 100644
index 0000000000..64e85f55e3
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.html
@@ -0,0 +1,76 @@
+<?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>TabSheetWithTabIds</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">TabSheetWithTabIds</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.components.tabsheet.TabSheetWithTabIds</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>tab1</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>tab2</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>tab3</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstabsheetTabSheetWithTabIds::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>tab1</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>tab2</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>tab3</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstabsheetTabSheetWithTabIds::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>tab1</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>tab2</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>tab3</td>
+ <td></td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.java
new file mode 100644
index 0000000000..ae5adea45e
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetWithTabIds.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ *
+ */
+package com.vaadin.tests.components.tabsheet;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.TabSheet.Tab;
+
+public class TabSheetWithTabIds extends AbstractTestUI {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ TabSheet tabSheet = new TabSheet();
+
+ final Tab tab1 = tabSheet.addTab(new Label("Label 1"), "Tab 1", null);
+
+ final Tab tab2 = tabSheet.addTab(new Label("Label 2"), "Tab 2", null);
+
+ final Tab tab3 = tabSheet.addTab(new Label("Label 3"), "Tab 3", null);
+
+ addComponent(tabSheet);
+
+ Button b = new Button("Set ids", new Button.ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ tab1.setId("tab1");
+ tab2.setId("tab2");
+ tab3.setId("tab3");
+ }
+ });
+ addComponent(b);
+
+ Button b2 = new Button("Clear ids", new Button.ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ tab1.setId(null);
+ tab2.setId(null);
+ tab3.setId(null);
+ }
+ });
+ addComponent(b2);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Add support for setId to TabSheet.Tab";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 12064;
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetTooltip.html b/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetTooltip.html
index d133ab9937..38ff6ab8ca 100644
--- a/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetTooltip.html
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabsheetTooltip.html
@@ -13,7 +13,7 @@
</thead><tbody>
<tr>
<td>open</td>
- <td>/run/com.vaadin.tests.components.tabsheet.TabsheetTooltip</td>
+ <td>/run/com.vaadin.tests.components.tabsheet.TabsheetTooltip?restartApplication</td>
<td></td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.html b/uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.html
new file mode 100644
index 0000000000..2428e8b4bb
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.html
@@ -0,0 +1,123 @@
+<?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.components.textfield.BigDecimalTextField?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertValue</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+ <td>15,2</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::PID_SLog_row_0</td>
+ <td>1. Commit ok. Property value: 15.2</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+ <td>70,12</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+ <td>130</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::</td>
+ <td>160,327</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::PID_SLog_row_0</td>
+ <td>2. Commit ok. Property value: 130</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+ <td>75,16</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+ <td>130abc</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::</td>
+ <td>96,280</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::PID_SLog_row_0</td>
+ <td>3. Commit failed: Commit failed</td>
+</tr>
+<tr>
+ <td>assertCSSClass</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td>
+ <td>v-errorindicator</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+ <td>71,12</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[1]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td>
+ <td>130,130</td>
+</tr>
+<tr>
+ <td>mouseClick</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::</td>
+ <td>118,280</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<!--The converter automatically removes trailing zeros-->
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentstextfieldBigDecimalTextField::PID_SLog_row_0</td>
+ <td>4. Commit ok. Property value: 130.13</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.java b/uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.java
new file mode 100644
index 0000000000..18d8679c2f
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/textfield/BigDecimalTextField.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.textfield;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Locale;
+
+import com.vaadin.data.fieldgroup.FieldGroup;
+import com.vaadin.data.util.BeanItem;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+
+/**
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class BigDecimalTextField extends AbstractTestUIWithLog {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ final VerticalLayout layout = new VerticalLayout();
+ layout.setMargin(true);
+ setLocale(new Locale("fi", "FI"));
+
+ BeanBigDecimal beanBigDecimal = new BeanBigDecimal();
+ BeanItem<BeanBigDecimal> beanItem = new BeanItem<BeanBigDecimal>(
+ beanBigDecimal);
+
+ FormLayout formLayout = new FormLayout();
+ TextField textField = new TextField("BigDecimal field");
+ textField.setImmediate(true);
+ textField.setValue("12");
+ formLayout.addComponent(textField);
+
+ final FieldGroup fieldGroup = new FieldGroup(beanItem);
+ fieldGroup.bind(textField, "decimal");
+
+ Button setValue = new Button("Set value to 15,2", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ ((TextField) fieldGroup.getField("decimal")).setValue("15,2");
+ }
+ });
+
+ Button button = new Button("Commit");
+ button.addClickListener(new Button.ClickListener() {
+ @Override
+ public void buttonClick(Button.ClickEvent event) {
+ try {
+ fieldGroup.commit();
+ log("Commit ok. Property value: "
+ + fieldGroup.getItemDataSource()
+ .getItemProperty("decimal").getValue());
+ } catch (FieldGroup.CommitException e) {
+ log("Commit failed: " + e.getMessage());
+ }
+ }
+ });
+
+ layout.addComponent(formLayout);
+ layout.addComponent(setValue);
+ layout.addComponent(button);
+
+ setContent(layout);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription()
+ */
+ @Override
+ protected String getTestDescription() {
+ return "Tests that BigDecimals work correctly with TextFields";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber()
+ */
+ @Override
+ protected Integer getTicketNumber() {
+ return 9997;
+ }
+
+ public static class BeanBigDecimal implements Serializable {
+ BigDecimal decimal;
+
+ public BeanBigDecimal() {
+
+ }
+
+ public BigDecimal getDecimal() {
+ return decimal;
+ }
+
+ public void setDecimal(BigDecimal decimal) {
+ this.decimal = decimal;
+ }
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/tree/SimpleTree.java b/uitest/src/com/vaadin/tests/components/tree/SimpleTree.java
index 2fd3f05dbb..06b8af1ec6 100644
--- a/uitest/src/com/vaadin/tests/components/tree/SimpleTree.java
+++ b/uitest/src/com/vaadin/tests/components/tree/SimpleTree.java
@@ -8,6 +8,8 @@ import com.vaadin.event.Action;
import com.vaadin.server.ThemeResource;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.AbstractSelect;
+import com.vaadin.ui.AbstractSelect.ItemDescriptionGenerator;
+import com.vaadin.ui.Component;
import com.vaadin.ui.Tree;
public class SimpleTree extends TestBase implements Action.Handler {
@@ -53,6 +55,17 @@ public class SimpleTree extends TestBase implements Action.Handler {
tree.setItemIcon(9, notCachedFolderIconLargeOther, "First Choice");
tree.setItemIcon(11, notCachedFolderIconLarge);
+ tree.setItemDescriptionGenerator(new ItemDescriptionGenerator() {
+ @Override
+ public String generateDescription(Component source, Object itemId,
+ Object propertyId) {
+ if ((Integer) itemId == 3) {
+ return "tree item tooltip";
+ }
+ return "";
+ }
+ });
+
// Expand whole tree
for (Object id : tree.rootItemIds()) {
tree.expandItemsRecursively(id);
diff --git a/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html b/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html
index e41cf5e176..338e4a2c5b 100644
--- a/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html
+++ b/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html
@@ -4,12 +4,12 @@
<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>
+<title>TooltipConfiguration</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
-<tr><td rowspan="1" colspan="3">New Test</td></tr>
+<tr><td rowspan="1" colspan="3">TooltipConfiguration</td></tr>
</thead><tbody>
<tr>
<td>open</td>
@@ -43,9 +43,9 @@
<td></td>
</tr>
<tr>
- <td>assertElementNotPresent</td>
+ <td>assertElementPositionLeft</td>
<td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
- <td></td>
+ <td>-1000</td>
</tr>
<!--Long close delay-->
<tr>
@@ -140,6 +140,7 @@
<td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
<td>100</td>
</tr>
+
</tbody></table>
</body>
</html>
diff --git a/uitest/src/com/vaadin/tests/components/uitest/UIScrollTest.html b/uitest/src/com/vaadin/tests/components/uitest/UIScrollTest.html
index b5326e4660..acbe30c5d2 100644
--- a/uitest/src/com/vaadin/tests/components/uitest/UIScrollTest.html
+++ b/uitest/src/com/vaadin/tests/components/uitest/UIScrollTest.html
@@ -43,7 +43,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>//div[@id='runcomvaadintestscomponentsuitestUIScrollTest-1797389287-overlays']/div</td>
+ <td>//div[@id='runcomvaadintestscomponentsuitestUIScrollTest-1797389287-overlays']//h1</td>
<td>Scrolled to 1020 px</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html
index 614ae7bcda..cdbcf8bacc 100644
--- a/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html
+++ b/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html
@@ -289,7 +289,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,8</td>
</tr>
<tr>
@@ -304,7 +304,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>11,6</td>
</tr>
<tr>
@@ -319,7 +319,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>8,5</td>
</tr>
<tr>
@@ -334,7 +334,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,6</td>
</tr>
<tr>
@@ -349,7 +349,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>10,7</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html
index 7d9ffc65b8..d5d70a62d2 100644
--- a/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html
+++ b/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html
@@ -289,7 +289,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,8</td>
</tr>
<tr>
@@ -304,7 +304,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>11,6</td>
</tr>
<tr>
@@ -319,7 +319,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>8,5</td>
</tr>
<tr>
@@ -334,7 +334,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,6</td>
</tr>
<tr>
@@ -349,7 +349,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>10,7</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html
index d0ee96c7ef..783784e993 100644
--- a/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html
+++ b/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html
@@ -289,7 +289,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,8</td>
</tr>
<tr>
@@ -304,7 +304,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>11,6</td>
</tr>
<tr>
@@ -319,7 +319,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>8,5</td>
</tr>
<tr>
@@ -334,7 +334,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,6</td>
</tr>
<tr>
@@ -349,7 +349,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>10,7</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html
index a330f5bf61..175def94d3 100644
--- a/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html
+++ b/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html
@@ -289,7 +289,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,8</td>
</tr>
<tr>
@@ -304,7 +304,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>11,6</td>
</tr>
<tr>
@@ -319,7 +319,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>8,5</td>
</tr>
<tr>
@@ -334,7 +334,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,6</td>
</tr>
<tr>
@@ -349,7 +349,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>10,7</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html
index 61ba58a0e6..0db6614a9c 100644
--- a/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html
+++ b/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html
@@ -289,7 +289,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,8</td>
</tr>
<tr>
@@ -304,7 +304,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>11,6</td>
</tr>
<tr>
@@ -319,7 +319,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>8,5</td>
</tr>
<tr>
@@ -334,7 +334,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>9,6</td>
</tr>
<tr>
@@ -349,7 +349,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>10,7</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html b/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html
index ac81dfdefb..ae77628bff 100644
--- a/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html
+++ b/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html
@@ -40,7 +40,7 @@
<!--Click close in title bar-->
<tr>
<td>click</td>
- <td>vaadin=runcomvaadintestscomponentswindowCloseSubWindow::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowCloseSubWindow::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td></td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.html b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.html
new file mode 100644
index 0000000000..f5f89d13ef
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.html
@@ -0,0 +1,136 @@
+<?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>ExtraWindowShownWaiAria</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">ExtraWindowShownWaiAria</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/com.vaadin.tests.components.window.ExtraWindowShownWaiAria?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/@role</td>
+ <td>dialog</td>
+</tr>
+<tr>
+ <td>storeAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]@id</td>
+ <td>headerid</td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/@aria-labelledby</td>
+ <td>${headerid}</td>
+</tr>
+<tr>
+ <td>storeAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/FocusableScrollPanel[0]/VCssLayout[0]/VLabel[0]@id</td>
+ <td>descriptionid</td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/@aria-describedby</td>
+ <td>${descriptionid}</td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]@role</td>
+ <td>button</td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]@role</td>
+ <td>button</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/FocusableScrollPanel[0]/VCssLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VCheckBox[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>storeAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/FocusableScrollPanel[0]/VCssLayout[0]/VLabel[0]@id</td>
+ <td>descriptionid</td>
+</tr>
+<tr>
+ <td>storeAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/FocusableScrollPanel[0]/VCssLayout[0]/VLabel[1]@id</td>
+ <td>description2id</td>
+</tr>
+<tr>
+ <td>assertAttribute</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/@aria-describedby</td>
+ <td>${descriptionid} ${description2id}</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/FocusableScrollPanel[0]/VCssLayout[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VWindow[0]/</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[6]/VTextField[0]</td>
+ <td>Important</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[7]/VTextField[0]</td>
+ <td> - do ASAP</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentswindowExtraWindowShownWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>xpath=//div[@class='v-window-header']/span[@class='v-assistive-device-only'][1]</td>
+ <td>Important</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>xpath=//div[@class='v-window-header']/span[@class='v-assistive-device-only'][2]</td>
+ <td>- do ASAP</td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java
new file mode 100644
index 0000000000..39989926e7
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java
@@ -0,0 +1,172 @@
+package com.vaadin.tests.components.window;
+
+import com.vaadin.server.ThemeResource;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.window.WindowState.WindowRole;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.CssLayout;
+import com.vaadin.ui.FormLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.Window;
+
+public class ExtraWindowShownWaiAria extends AbstractTestUI {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ final CheckBox modal = new CheckBox("Modal dialog");
+ modal.setTabIndex(7);
+ final CheckBox additionalDescription = new CheckBox(
+ "Additional Description");
+ final CheckBox tabStop = new CheckBox(
+ "Prevent leaving window with Tab key");
+ final CheckBox tabOrder = new CheckBox("Change Taborder");
+ final TextField prefix = new TextField("Prefix: ");
+ final TextField postfix = new TextField("Postfix: ");
+
+ final TextField topTabStopMessage = new TextField(
+ "Top Tab Stop Message");
+ final TextField bottomTabStopMessage = new TextField(
+ "Bottom Tab Stop Message");
+
+ Button simple = new Button("Open Alert Dialog",
+ new Button.ClickListener() {
+
+ @Override
+ public void buttonClick(ClickEvent event) {
+ CssLayout layout = new CssLayout();
+
+ final Window w = new Window("Sub window", layout);
+ w.center();
+ w.setModal(modal.getValue());
+ w.setAssistiveRole(WindowRole.ALERTDIALOG);
+ w.setAssistivePrefix(prefix.getValue());
+ w.setAssistivePostfix(postfix.getValue());
+
+ Label description1 = new Label("Simple alert dialog.");
+ layout.addComponent(description1);
+
+ if (!additionalDescription.getValue()) {
+ w.setAssistiveDescription(description1);
+ } else {
+ Label description2 = new Label(
+ "Please select what to do!");
+ layout.addComponent(description2);
+
+ w.setAssistiveDescription(description1,
+ description2);
+ }
+
+ w.setTabStopEnabled(tabStop.getValue());
+ w.setTabStopTopAssistiveText(topTabStopMessage
+ .getValue());
+ w.setTabStopBottomAssistiveText(bottomTabStopMessage
+ .getValue());
+
+ Button close = new Button("Close",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ w.close();
+ }
+ });
+ layout.addComponent(close);
+ Button iconButton = new Button("A button with icon");
+ iconButton.setIcon(new ThemeResource(
+ "../runo/icons/16/ok.png"));
+ layout.addComponent(iconButton);
+
+ event.getButton().getUI().addWindow(w);
+ iconButton.focus();
+
+ if (tabOrder.getValue()) {
+ close.setTabIndex(5);
+ }
+ }
+
+ });
+ getLayout().addComponent(simple);
+
+ Button complex = new Button("Open Entry Dialog",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ FormLayout form = new FormLayout();
+
+ final Window w = new Window("Form Window", form);
+ w.center();
+ w.setModal(modal.getValue());
+ w.setAssistivePrefix(prefix.getValue());
+ w.setAssistivePostfix(postfix.getValue());
+
+ Label description1 = new Label(
+ "Please fill in your data");
+ form.addComponent(description1);
+
+ if (!additionalDescription.getValue()) {
+ w.setAssistiveDescription(description1);
+ } else {
+ Label description2 = new Label(
+ "and press the button save.");
+ form.addComponent(description2);
+
+ w.setAssistiveDescription(description1,
+ description2);
+ }
+
+ w.setTabStopEnabled(tabStop.getValue());
+ w.setTabStopTopAssistiveText(topTabStopMessage
+ .getValue());
+ w.setTabStopBottomAssistiveText(bottomTabStopMessage
+ .getValue());
+
+ TextField name = new TextField("Name:");
+ form.addComponent(name);
+
+ form.addComponent(new TextField("Address"));
+
+ Button saveButton = new Button("Save",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ w.close();
+ }
+ });
+ form.addComponent(saveButton);
+
+ event.getButton().getUI().addWindow(w);
+ name.focus();
+
+ if (tabOrder.getValue()) {
+ name.setTabIndex(5);
+ }
+ }
+ });
+ getLayout().addComponent(complex);
+
+ getLayout().addComponent(modal);
+ getLayout().addComponent(additionalDescription);
+ getLayout().addComponent(tabStop);
+ getLayout().addComponent(tabOrder);
+
+ getLayout().addComponent(prefix);
+ getLayout().addComponent(postfix);
+
+ getLayout().addComponent(topTabStopMessage);
+ getLayout().addComponent(bottomTabStopMessage);
+
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Test for WAI-ARIA implementation";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 11821;
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html b/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html
index 8374a90b52..6fd99caa19 100644
--- a/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html
+++ b/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html
@@ -90,7 +90,7 @@
<!--Close window 4, which is the topmost window-->
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[3]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[3]/domChild[0]/domChild[0]/domChild[3]</td>
<td>11,15</td>
</tr>
<tr>
@@ -101,7 +101,7 @@
<!--Close Dialog 3 (topmost)-->
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[3]</td>
<td>6,8</td>
</tr>
<!--Make Dialog 5 (topmost) non-modal-->
@@ -139,7 +139,7 @@
<!--Close dialog 5-->
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[3]</td>
<td>10,5</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/window/TooltipInWindow.html b/uitest/src/com/vaadin/tests/components/window/TooltipInWindow.html
index 63e371e379..575eb652b7 100644
--- a/uitest/src/com/vaadin/tests/components/window/TooltipInWindow.html
+++ b/uitest/src/com/vaadin/tests/components/window/TooltipInWindow.html
@@ -44,9 +44,9 @@
<td></td>
</tr>
<tr>
- <td>assertElementNotPresent</td>
+ <td>assertElementPositionLeft</td>
<td>vaadin=runcomvaadintestscomponentswindowTooltipInWindow::Root/VTooltip[0]</td>
- <td></td>
+ <td>-1000</td>
</tr>
<!--Show tooltip in Window-->
<tr>
@@ -76,10 +76,11 @@
<td></td>
</tr>
<tr>
- <td>assertElementNotPresent</td>
+ <td>assertElementPositionLeft</td>
<td>vaadin=runcomvaadintestscomponentswindowTooltipInWindow::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td>
- <td></td>
+ <td>-1000</td>
</tr>
+
</tbody></table>
</body>
</html>
diff --git a/uitest/src/com/vaadin/tests/components/window/WindowCaptionTest.html b/uitest/src/com/vaadin/tests/components/window/WindowCaptionTest.html
index a9a6fd621e..85ab67e4f6 100644
--- a/uitest/src/com/vaadin/tests/components/window/WindowCaptionTest.html
+++ b/uitest/src/com/vaadin/tests/components/window/WindowCaptionTest.html
@@ -3,7 +3,6 @@
<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:8888/" />
<title>New Test</title>
</head>
<body>
@@ -18,7 +17,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowTest::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowTest::PID_StestComponent/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td>Short</td>
</tr>
<tr>
@@ -43,7 +42,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowTest::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowTest::PID_StestComponent/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td>This is a semi-long text that might wrap.</td>
</tr>
<tr>
@@ -68,7 +67,7 @@
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowTest::PID_StestComponent/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowTest::PID_StestComponent/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td></td>
</tr>
diff --git a/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html b/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html
index dcdfa05687..a27963a066 100644
--- a/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html
+++ b/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html
@@ -3,7 +3,6 @@
<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/run/" />
<title>WindowMaximizeRestoreTest</title>
</head>
<body>
@@ -19,59 +18,59 @@
<!--Test maximize-restore button-->
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-maximizebox</td>
</tr>
<tr>
<td>assertText</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td>Window 1</td>
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>7,8</td>
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-restorebox</td>
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>9,7</td>
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-maximizebox</td>
</tr>
<!--test double click on header-->
<tr>
<td>doubleClickAt</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td></td>
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-restorebox</td>
</tr>
<tr>
<td>doubleClickAt</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td></td>
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-maximizebox</td>
</tr>
<!--Resizable = false should hide max-restore button-->
<tr>
<td>assertVisible</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td></td>
</tr>
<tr>
@@ -81,7 +80,7 @@
</tr>
<tr>
<td>assertNotVisible</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td></td>
</tr>
<!--Test server side max-restore-->
@@ -92,7 +91,7 @@
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-restorebox</td>
</tr>
<tr>
@@ -102,28 +101,28 @@
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-maximizebox</td>
</tr>
<!--test double click on header doesn't work-->
<tr>
<td>doubleClickAt</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td></td>
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-maximizebox</td>
</tr>
<tr>
<td>doubleClickAt</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td></td>
</tr>
<tr>
<td>assertCSSClass</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>v-window-maximizebox</td>
</tr>
<tr>
@@ -149,7 +148,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>10,8</td>
</tr>
<tr>
@@ -175,7 +174,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[2]</td>
<td>6,11</td>
</tr>
<tr>
@@ -185,7 +184,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[3]</td>
<td>7,5</td>
</tr>
<tr>
@@ -210,12 +209,12 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[2]</td>
<td>6,11</td>
</tr>
<tr>
<td>doubleClickAt</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td>113,10</td>
</tr>
<tr>
@@ -226,27 +225,27 @@
<!--Test that size and position is preserved when maximizing and restoring-->
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>8,4</td>
</tr>
<tr>
<td>dragAndDrop</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td>
<td>-200,-200</td>
</tr>
<tr>
<td>dragAndDrop</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[4]/domChild[0]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[5]/domChild[0]</td>
<td>+100,+100</td>
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>6,5</td>
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
<td>5,8</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html b/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html
index fa63e5e1e6..3ea1f8f732 100644
--- a/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html
+++ b/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html
@@ -18,7 +18,7 @@
</tr>
<tr>
<td>mouseClick</td>
- <td>vaadin=runcomvaadintestscomponentswindowWindowWithInvalidCloseListener::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>vaadin=runcomvaadintestscomponentswindowWindowWithInvalidCloseListener::/VWindow[0]/domChild[0]/domChild[0]/domChild[3]</td>
<td>6,7</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/fieldgroup/FieldBinderWithBeanValidation.java b/uitest/src/com/vaadin/tests/fieldgroup/FieldBinderWithBeanValidation.java
index 4f83f5d0fd..2c202af02b 100644
--- a/uitest/src/com/vaadin/tests/fieldgroup/FieldBinderWithBeanValidation.java
+++ b/uitest/src/com/vaadin/tests/fieldgroup/FieldBinderWithBeanValidation.java
@@ -7,7 +7,6 @@ import com.vaadin.data.util.BeanItem;
import com.vaadin.tests.components.TestBase;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.Country;
-import com.vaadin.tests.data.bean.Person;
import com.vaadin.tests.data.bean.PersonWithBeanValidationAnnotations;
import com.vaadin.tests.data.bean.Sex;
import com.vaadin.tests.util.Log;
@@ -90,8 +89,10 @@ public class FieldBinderWithBeanValidation extends TestBase {
p));
}
- public static Person getPerson(FieldGroup binder) {
- return ((BeanItem<Person>) binder.getItemDataSource()).getBean();
+ public static PersonWithBeanValidationAnnotations getPerson(
+ FieldGroup binder) {
+ return ((BeanItem<PersonWithBeanValidationAnnotations>) binder
+ .getItemDataSource()).getBean();
}
@Override
diff --git a/uitest/src/com/vaadin/tests/fieldgroup/IntegerRangeValidator.html b/uitest/src/com/vaadin/tests/fieldgroup/IntegerRangeValidator.html
index b7c40b4d9e..7c6f9ceb39 100644
--- a/uitest/src/com/vaadin/tests/fieldgroup/IntegerRangeValidator.html
+++ b/uitest/src/com/vaadin/tests/fieldgroup/IntegerRangeValidator.html
@@ -1,17 +1,17 @@
- <?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>
+<?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:88888/" />
+<title>IntegerRangeValidator</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">IntegerRangeValidator</td></tr>
+</thead><tbody>
+<tr>
<td>open</td>
<td>/run/com.vaadin.tests.fieldgroup.BasicPersonForm?restartApplication</td>
<td></td>
@@ -82,12 +82,7 @@
</tr>
<tr>
<td>showTooltip</td>
- <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[5]/VTextField[0]</td>
- <td></td>
-</tr>
-<tr>
- <td>waitForElementPresent</td>
- <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::Root/VTooltip[0]</td>
+ <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[5]/VTextField[0]</td>
<td></td>
</tr>
<tr>
@@ -95,7 +90,7 @@
<td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::Root/VTooltip[0]/FlowPanel[0]/VErrorMessage[0]/HTML[0]</td>
<td>Must be between 0 and 150, -1 is not</td>
</tr>
-</tbody></table>
- </body>
- </html>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7a1/FormatTableValue.java b/uitest/src/com/vaadin/tests/minitutorials/v7a1/FormatTableValue.java
index be2768a5f7..8485bba499 100644
--- a/uitest/src/com/vaadin/tests/minitutorials/v7a1/FormatTableValue.java
+++ b/uitest/src/com/vaadin/tests/minitutorials/v7a1/FormatTableValue.java
@@ -3,7 +3,7 @@ package com.vaadin.tests.minitutorials.v7a1;
import java.text.NumberFormat;
import java.util.Locale;
-import com.vaadin.data.util.converter.StringToNumberConverter;
+import com.vaadin.data.util.converter.StringToDoubleConverter;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Table;
@@ -30,14 +30,14 @@ public class FormatTableValue extends AbstractTestUI {
table.getItem(itemId).getItemProperty(DEFAULT_PROPERTY)
.setValue(3.1415);
- table.setConverter(PERCENT_PROPERTY, new StringToNumberConverter() {
+ table.setConverter(PERCENT_PROPERTY, new StringToDoubleConverter() {
@Override
protected NumberFormat getFormat(Locale locale) {
return NumberFormat.getPercentInstance(locale);
}
});
- table.setConverter(CURRENCY_PROPERTY, new StringToNumberConverter() {
+ table.setConverter(CURRENCY_PROPERTY, new StringToDoubleConverter() {
@Override
protected NumberFormat getFormat(Locale locale) {
return NumberFormat.getCurrencyInstance(locale);
diff --git a/uitest/src/com/vaadin/tests/util/TestUtils.java b/uitest/src/com/vaadin/tests/util/TestUtils.java
index 5c6315a23a..dcd28c3413 100644
--- a/uitest/src/com/vaadin/tests/util/TestUtils.java
+++ b/uitest/src/com/vaadin/tests/util/TestUtils.java
@@ -99,22 +99,13 @@ public class TestUtils {
"YE", "ZAMBIA", "ZM", "ZIMBABWE", "ZW" };
/**
- * Crossbrowser hack to dynamically add css current window. Can be used to
- * keep tests css in source files.
+ * Injects css into the current window. Can be used to keep tests css in
+ * source files.
*
* @param cssString
*/
public static void injectCSS(UI w, String cssString) {
- String script = "if ('\\v'=='v') /* ie only */ {\n"
- + " document.createStyleSheet().cssText = '"
- + cssString
- + "';\n"
- + " } else {var tag = document.createElement('style'); tag.type = 'text/css';"
- + " document.getElementsByTagName('head')[0].appendChild(tag);tag[ (typeof "
- + "document.body.style.WebkitAppearance=='string') /* webkit only */ ? 'innerText' "
- + ": 'innerHTML'] = '" + cssString + "';}";
-
- w.getPage().getJavaScript().execute(script);
+ w.getPage().getStyles().add(cssString);
}
public static void installPerformanceReporting(TextArea targetTextArea) {