diff options
Diffstat (limited to 'src/com/vaadin/terminal')
24 files changed, 756 insertions, 353 deletions
diff --git a/src/com/vaadin/terminal/FileResource.java b/src/com/vaadin/terminal/FileResource.java index 46f1a6c028..571622b0e4 100644 --- a/src/com/vaadin/terminal/FileResource.java +++ b/src/com/vaadin/terminal/FileResource.java @@ -10,6 +10,7 @@ import java.io.FileNotFoundException; import com.vaadin.Application; import com.vaadin.service.FileTypeResolver; +import com.vaadin.terminal.Terminal.ErrorEvent; /** * <code>FileResources</code> are files or directories on local filesystem. The @@ -67,7 +68,15 @@ public class FileResource implements ApplicationResource { ds.setCacheTime(cacheTime); return ds; } catch (final FileNotFoundException e) { - // No logging for non-existing files at this level. + // Log the exception using the application error handler + getApplication().getErrorHandler().terminalError(new ErrorEvent() { + + public Throwable getThrowable() { + return e; + } + + }); + return null; } } diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java index 39f4e845f7..5bcf613f6d 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConfiguration.java @@ -148,20 +148,15 @@ public class ApplicationConfiguration { int lastdot = module.lastIndexOf("."); String base = module.substring(0, lastdot); String simpleName = module.substring(lastdot + 1); - // if (!wsname.startsWith(base) || !wsname.endsWith(simpleName)) { - // // WidgetSet module name does not match implementation name; - // // probably inherited WidgetSet with entry-point. Skip. - // GWT.log("Ignored init for " + wsname + " when starting " + module, - // null); - // return; - // } if (initedWidgetSet != null) { - // Something went wrong: multiple widgetsets inited - String msg = "Tried to init " + widgetset.getClass().getName() - + ", but " + initedWidgetSet.getClass().getName() - + " was already inited."; - ApplicationConnection.getConsole().log(msg); + // Multiple widgetsets inited; can happen with custom WS + entry + // point + String msg = "Ignoring " + widgetset.getClass().getName() + + ", because " + initedWidgetSet.getClass().getName() + + " was already inited (if this is wrong, your entry point" + + " is probably not first your .gwt.xml)."; + throw new IllegalStateException(msg); } initedWidgetSet = widgetset; ArrayList<String> appIds = new ArrayList<String>(); diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 34e3580608..8574bc0476 100755 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -1116,7 +1116,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, Paintable newValue, boolean immediate) { @@ -1139,7 +1139,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1162,7 +1162,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1186,7 +1186,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1210,7 +1210,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1234,7 +1234,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1258,7 +1258,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, @@ -1282,7 +1282,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, Map<String, Object> map, boolean immediate) { @@ -1347,7 +1347,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, String[] values, boolean immediate) { @@ -1382,7 +1382,7 @@ public class ApplicationConnection { * @param newValue * the new value to be sent * @param immediate - * true if the update is to be sent as suun as possible + * true if the update is to be sent as soon as possible */ public void updateVariable(String paintableId, String variableName, Object[] values, boolean immediate) { diff --git a/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java b/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java index 724bd24a69..f1b01509d4 100644 --- a/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java +++ b/src/com/vaadin/terminal/gwt/client/ClientExceptionHandler.java @@ -3,7 +3,7 @@ package com.vaadin.terminal.gwt.client; public class ClientExceptionHandler {
public static void displayError(Throwable e) {
- displayError(e.getMessage());
+ displayError(e.getClass().getName() + ": " + e.getMessage());
e.printStackTrace();
}
diff --git a/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java b/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java index 8ee6b0bb14..c34e5d8bb3 100644 --- a/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java +++ b/src/com/vaadin/terminal/gwt/client/DefaultWidgetSet.java @@ -38,7 +38,14 @@ public class DefaultWidgetSet implements WidgetSet { * This is the entry point method. It will start the first */ public void onModuleLoad() { - ApplicationConfiguration.initConfigurations(this); + try { + ApplicationConfiguration.initConfigurations(this); + } catch (Exception e) { + // Log & don't continue; + // custom WidgetSets w/ entry points will cause this + ApplicationConnection.getConsole().log(e.getMessage()); + return; + } ApplicationConfiguration.startNextApplication(); // start first app map = GWT.create(WidgetMap.class); } diff --git a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java index 45b8c9353b..172e4df627 100755 --- a/src/com/vaadin/terminal/gwt/client/VDebugConsole.java +++ b/src/com/vaadin/terminal/gwt/client/VDebugConsole.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Set; import com.google.gwt.core.client.JsArray; +import com.google.gwt.dom.client.Style.FontWeight; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.DOM; @@ -304,6 +305,10 @@ public final class VDebugConsole extends VOverlay implements Console { * @see com.vaadin.terminal.gwt.client.Console#log(java.lang.String) */ public void log(String msg) { + if (msg == null) { + msg = "null"; + } + panel.add(new HTML(msg)); System.out.println(msg); consoleLog(msg); @@ -315,7 +320,14 @@ public final class VDebugConsole extends VOverlay implements Console { * @see com.vaadin.terminal.gwt.client.Console#error(java.lang.String) */ public void error(String msg) { - panel.add((new HTML(msg))); + if (msg == null) { + msg = "null"; + } + + HTML html = new HTML(msg); + html.getElement().getStyle().setColor("#f00"); + html.getElement().getStyle().setFontWeight(FontWeight.BOLD); + panel.add(html); System.err.println(msg); consoleErr(msg); } @@ -327,7 +339,13 @@ public final class VDebugConsole extends VOverlay implements Console { * Object) */ public void printObject(Object msg) { - panel.add((new Label(msg.toString()))); + String str; + if (msg == null) { + str = "null"; + } else { + str = msg.toString(); + } + panel.add((new Label(str))); consoleLog(msg.toString()); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java index bf13530d7f..90cf102cb9 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VCssLayout.java @@ -184,10 +184,11 @@ public class VCssLayout extends SimplePanel implements Paintable, Container { // loop oldWidgetWrappers that where not re-attached and unregister // them - for (final Iterator<Widget> it = oldWidgets.iterator(); it - .hasNext();) { - final Paintable w = (Paintable) it.next(); - client.unregisterPaintable(w); + for (Widget w : oldWidgets) { + if (w instanceof Paintable) { + final Paintable p = (Paintable) w; + client.unregisterPaintable(p); + } widgetToCaption.remove(w); } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java index 7d69e5682a..ffd09dab2c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VEmbedded.java @@ -140,6 +140,7 @@ public class VEmbedded extends HTML implements Paintable { parameters.put("movie", getSrc(uidl, client)); } + // Add the parameters to the Object for (String name : parameters.keySet()) { html += "<param name=\"" + escapeAttribute(name) + "\" value=\"" @@ -147,9 +148,15 @@ public class VEmbedded extends HTML implements Paintable { } html += "<embed src=\"" + getSrc(uidl, client) + "\" width=\"" - + width + "\" height=\"" + height + "\"></embed>"; + + width + "\" height=\"" + height + "\" "; - html += "</object>"; + // Add the parameters to the Embed + for (String name : parameters.keySet()) { + html += escapeAttribute(name) + "=\"" + + escapeAttribute(parameters.get(name)) + "\" "; + } + + html += "></embed></object>"; setHTML(html); } else if (mime.equals("image/svg+xml")) { String data; diff --git a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java index 2dcf7562ae..669b5b90ea 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VFilterSelect.java @@ -631,6 +631,23 @@ public class VFilterSelect extends Composite implements Paintable, Field, public void onLoad(LoadEvent event) { updateRootWidth(); updateSelectedIconPosition(); + + /* + * We need to re-calculate the widths in IE at load time to + * ensure that the text is not placed behind the icon. #3991 + */ + if (BrowserInfo.get().isIE()) { + int tbWidth = Util.getRequiredWidth(tb); + int openerWidth = Util.getRequiredWidth(popupOpener); + int iconWidth = selectedItemIcon.isAttached() ? Util + .measureMarginLeft(tb.getElement()) + - Util.measureMarginLeft(selectedItemIcon + .getElement()) : 0; + + int w = tbWidth + openerWidth + iconWidth; + tb.setWidth((tbWidth - getTextboxPadding()) + "px"); + setTextboxWidth(w); + } } }); @@ -883,8 +900,8 @@ public class VFilterSelect extends Composite implements Paintable, Field, panel.remove(selectedItemIcon); updateRootWidth(); } else { - selectedItemIcon.setUrl(iconUri); panel.insert(selectedItemIcon, 0); + selectedItemIcon.setUrl(iconUri); updateRootWidth(); updateSelectedIconPosition(); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java index 0a28fd6b41..75ecbd3249 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VMenuBar.java @@ -8,11 +8,14 @@ import java.util.Stack; import com.google.gwt.dom.client.NodeList; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.History; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.HasHTML; import com.google.gwt.user.client.ui.PopupPanel; @@ -27,7 +30,8 @@ import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; public class VMenuBar extends Widget implements Paintable, - CloseHandler<PopupPanel>, ContainerResizedListener { +CloseHandler<PopupPanel>, ContainerResizedListener, +ValueChangeHandler<String> { /** Set the CSS class name to allow styling. */ public static final String CLASSNAME = "v-menubar"; @@ -76,6 +80,8 @@ public class VMenuBar extends Widget implements Paintable, if (!subMenu) { setStylePrimaryName(CLASSNAME); + // Monitor back&forward buttons + History.addValueChangeHandler(this); } else { setStylePrimaryName(CLASSNAME + "-submenu"); } @@ -866,4 +872,12 @@ public class VMenuBar extends Widget implements Paintable, return w; } + public void onValueChange(ValueChangeEvent<String> arg0) { + // Close menu if user uses back & forward buttons #4109 + if (!subMenu) { + setSelected(null); + hideChildren(); + menuVisible = false; + } + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java b/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java index e544e1f9ce..ef7e183698 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VNativeButton.java @@ -19,7 +19,7 @@ import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VTooltip; -public class VNativeButton extends Button implements Paintable { +public class VNativeButton extends Button implements Paintable, ClickHandler { public static final String CLASSNAME = "v-nativebutton"; @@ -48,18 +48,8 @@ public class VNativeButton extends Button implements Paintable { getElement().appendChild(captionElement); captionElement.setClassName(getStyleName() + "-caption"); - addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - if (id == null || client == null) { - return; - } - if (BrowserInfo.get().isSafari()) { - VNativeButton.this.setFocus(true); - } - client.updateVariable(id, "state", true, true); - clickPending = false; - } - }); + addClickHandler(this); + sinkEvents(VTooltip.TOOLTIP_EVENTS); sinkEvents(Event.ONMOUSEDOWN); sinkEvents(Event.ONMOUSEUP); @@ -179,4 +169,24 @@ public class VNativeButton extends Button implements Paintable { } } + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event + * .dom.client.ClickEvent) + */ + public void onClick(ClickEvent event) { + if (id == null || client == null) { + return; + } + + if (BrowserInfo.get().isSafari()) { + VNativeButton.this.setFocus(true); + } + + client.updateVariable(id, "state", true, true); + clickPending = false; + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java index f9da8f67d9..d41a739655 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPanel.java @@ -189,7 +189,6 @@ public class VPanel extends SimplePanel implements Container { } layout.updateFromUIDL(layoutUidl, client); - runHacks(false); // We may have actions attached to this panel if (uidl.getChildCount() > 1) { final int cnt = uidl.getChildCount(); @@ -224,6 +223,10 @@ public class VPanel extends SimplePanel implements Container { scrollLeft = contentNode.getScrollLeft(); } + // Must be run after scrollTop is set as Webkit overflow fix re-sets the + // scrollTop + runHacks(false); + rendering = false; } @@ -393,7 +396,8 @@ public class VPanel extends SimplePanel implements Container { super.setHeight(height); if (height != null && height != "") { final int targetHeight = getOffsetHeight(); - int containerHeight = targetHeight - captionNode.getOffsetHeight() + int containerHeight = targetHeight + - captionNode.getParentElement().getOffsetHeight() - bottomDecoration.getOffsetHeight() - getContainerBorderHeight(); if (containerHeight < 0) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java index 8d83e8b2b5..9b504bc5f3 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VPopupCalendar.java @@ -52,7 +52,6 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
boolean lastReadOnlyState = readonly;
super.updateFromUIDL(uidl, client);
- addStyleName(CLASSNAME + "-popupcalendar");
popup.setStyleName(VDateField.CLASSNAME + "-popup "
+ VDateField.CLASSNAME + "-"
+ resolutionToString(currentResolution));
@@ -67,6 +66,12 @@ public class VPopupCalendar extends VTextualDate implements Paintable, Field, }
+ @Override
+ public void setStyleName(String style) {
+ // make sure the style is there before size calculation
+ super.setStyleName(style + " " + CLASSNAME + "-popupcalendar");
+ }
+
public void onClick(ClickEvent event) {
if (event.getSource() == calendarToggle && !open && !readonly) {
open = true;
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java index 59c5d252fe..53f0033bcd 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java @@ -19,11 +19,14 @@ import com.google.gwt.dom.client.TableRowElement; import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.event.dom.client.ScrollEvent; import com.google.gwt.event.dom.client.ScrollHandler; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.History; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.FlowPanel; @@ -72,7 +75,7 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; * TODO implement unregistering for child components in Cells */ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, - VHasDropHandler { + VHasDropHandler, ValueChangeHandler<String> { public static final String CLASSNAME = "v-table"; public static final String ITEM_CLICK_EVENT_ID = "itemClick"; @@ -168,6 +171,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, rowRequestHandler = new RowRequestHandler(); + // Handle back & forward browser buttons + History.addValueChangeHandler(this); } @SuppressWarnings("unchecked") @@ -3264,4 +3269,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, } + public void onValueChange(ValueChangeEvent<String> arg0) { + client.getContextMenu().hide(); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java index d7a11427fb..101c1e9848 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VSplitPanel.java @@ -122,6 +122,9 @@ public class VSplitPanel extends ComplexPanel implements Container, private boolean rendering = false; + /* The current position of the split handle in either percentages or pixels */ + private String position; + public VSplitPanel() { this(ORIENTATION_HORIZONTAL); } @@ -208,7 +211,8 @@ public class VSplitPanel extends ComplexPanel implements Container, setStylenames(); - setSplitPosition(uidl.getStringAttribute("position")); + position = uidl.getStringAttribute("position"); + setSplitPosition(position); final Paintable newFirstChild = client.getPaintable(uidl .getChildUIDL(0)); @@ -257,14 +261,26 @@ public class VSplitPanel extends ComplexPanel implements Container, } private void setSplitPosition(String pos) { + if (pos == null) { + return; + } + + // Convert percentage values to pixels + if (pos.indexOf("%") > 0) { + pos = Float.parseFloat(pos.substring(0, pos.length() - 1)) + / 100 + * (orientation == ORIENTATION_HORIZONTAL ? getOffsetWidth() + : getOffsetHeight()) + "px"; + } + if (orientation == ORIENTATION_HORIZONTAL) { DOM.setStyleAttribute(splitter, "left", pos); } else { DOM.setStyleAttribute(splitter, "top", pos); } + iLayout(); client.runDescendentsLayout(this); - } /* @@ -441,9 +457,6 @@ public class VSplitPanel extends ComplexPanel implements Container, onVerticalMouseMove(y); break; } - iLayout(); - // TODO Check if this is needed - client.runDescendentsLayout(this); } @@ -455,7 +468,18 @@ public class VSplitPanel extends ComplexPanel implements Container, if (newX + getSplitterSize() > getOffsetWidth()) { newX = getOffsetWidth() - getSplitterSize(); } - DOM.setStyleAttribute(splitter, "left", newX + "px"); + + if (position.indexOf("%") > 0) { + float pos = newX; + // 100% needs special handling + if (newX + getSplitterSize() >= getOffsetWidth()) { + pos = getOffsetWidth(); + } + position = pos / getOffsetWidth() * 100 + "%"; + } + + setSplitPosition(newX + "px"); + if (origX != newX) { resized = true; } @@ -470,7 +494,18 @@ public class VSplitPanel extends ComplexPanel implements Container, if (newY + getSplitterSize() > getOffsetHeight()) { newY = getOffsetHeight() - getSplitterSize(); } - DOM.setStyleAttribute(splitter, "top", newY + "px"); + + if (position.indexOf("%") > 0) { + float pos = newY; + // 100% needs special handling + if (newY + getSplitterSize() >= getOffsetHeight()) { + pos = getOffsetHeight(); + } + position = pos / getOffsetHeight() * 100 + "%"; + } + + setSplitPosition(newY + "px"); + if (origY != newY) { resized = true; } @@ -554,9 +589,9 @@ public class VSplitPanel extends ComplexPanel implements Container, this.height = height; super.setHeight(height); + if (!rendering && client != null) { - iLayout(); - client.runDescendentsLayout(this); + setSplitPosition(position); } } @@ -568,9 +603,9 @@ public class VSplitPanel extends ComplexPanel implements Container, this.width = width; super.setWidth(width); + if (!rendering && client != null) { - iLayout(); - client.runDescendentsLayout(this); + setSplitPosition(position); } } @@ -627,12 +662,14 @@ public class VSplitPanel extends ComplexPanel implements Container, * Updates the new split position back to server. */ private void updateSplitPositionToServer() { - // We always send pixel values to server - final String position = orientation == ORIENTATION_HORIZONTAL ? splitter - .getStyle().getProperty("left") - : splitter.getStyle().getProperty("top"); - final int pos = Integer.parseInt(position.substring(0, position - .length() - 2)); + int pos = 0; + if (position.indexOf("%") > 0) { + pos = Float.valueOf(position.substring(0, position.length() - 1)) + .intValue(); + } else { + pos = Integer + .parseInt(position.substring(0, position.length() - 2)); + } client.updateVariable(id, "position", pos, immediate); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java index fe2c03496c..f4bc96f66a 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java @@ -218,6 +218,7 @@ public class VTextField extends TextBoxBase implements Paintable, Field, if (prompting) { setText(""); removeStyleDependentName(CLASSNAME_PROMPT); + setPrompting(false); if (BrowserInfo.get().isIE6()) { // IE6 does not show the cursor when tabbing into the field setCursorPos(0); diff --git a/src/com/vaadin/terminal/gwt/client/ui/VView.java b/src/com/vaadin/terminal/gwt/client/ui/VView.java index 838b60a6d8..033d35dab1 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VView.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VView.java @@ -372,10 +372,6 @@ public class VView extends SimplePanel implements Container, ResizeHandler, onResize(Window.getClientWidth(), Window.getClientHeight()); - if (BrowserInfo.get().isSafari()) { - Util.runWebkitOverflowAutoFix(getElement()); - } - // finally set scroll position from UIDL if (uidl.hasVariable("scrollTop")) { scrollable = true; @@ -387,6 +383,12 @@ public class VView extends SimplePanel implements Container, ResizeHandler, scrollable = false; } + // Safari workaround must be run after scrollTop is updated as it sets + // scrollTop using a deferred command. + if (BrowserInfo.get().isSafari()) { + Util.runWebkitOverflowAutoFix(getElement()); + } + rendering = false; } diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java index 428a9b38b9..148474bccc 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java @@ -14,6 +14,7 @@ import java.security.GeneralSecurityException; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -28,12 +29,10 @@ import javax.portlet.PortalContext; import javax.portlet.PortletConfig; import javax.portlet.PortletContext; import javax.portlet.PortletException; -import javax.portlet.PortletMode; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.PortletURL; -import javax.portlet.RenderMode; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import javax.portlet.ResourceRequest; @@ -47,9 +46,14 @@ import com.vaadin.Application.SystemMessages; import com.vaadin.external.org.apache.commons.fileupload.portlet.PortletFileUpload; import com.vaadin.terminal.DownloadStream; import com.vaadin.terminal.Terminal; +import com.vaadin.terminal.gwt.client.ApplicationConfiguration; +import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.ui.Window; /** + * Portlet 2.0 base class. This replaces the servlet in servlet/portlet 1.0 + * deployments and handles various portlet requests from the browser. + * * TODO Document me! * * @author peholmst @@ -193,7 +197,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet * the Default to be used. * @return String value or default if not found */ - private String getApplicationOrSystemProperty(String parameterName, + protected String getApplicationOrSystemProperty(String parameterName, String defaultValue) { String val = null; @@ -240,7 +244,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet } } - enum RequestType { + protected enum RequestType { FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN; } @@ -304,7 +308,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet RequestType requestType = getRequestType(request); if (requestType == RequestType.UNKNOWN) { - System.err.println("Unknown request type"); + handleUnknownRequest(request, response); } else if (requestType == RequestType.DUMMY) { /* * This dummy page is used by action responses to redirect to, in @@ -317,8 +321,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet final PrintWriter outWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out, "UTF-8"))); outWriter.print("<html><body>dummy page</body></html>"); - outWriter.flush(); - out.close(); + outWriter.close(); } else if (requestType == RequestType.STATIC_FILE) { serveStaticResources((ResourceRequest) request, (ResourceResponse) response); @@ -386,20 +389,43 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet /* Notify listeners */ + // Finds the window within the application + Window window = null; + synchronized (application) { + if (application.isRunning()) { + switch (requestType) { + case FILE_UPLOAD: + // no window + break; + case APPLICATION_RESOURCE: + // use main window - should not need any window + window = application.getMainWindow(); + break; + default: + window = applicationManager.getApplicationWindow( + request, this, application, null); + } + // if window not found, not a problem - use null + } + } + // TODO Should this happen before or after the transaction // starts? if (request instanceof RenderRequest) { applicationContext.firePortletRenderRequest(application, - (RenderRequest) request, (RenderResponse) response); + window, (RenderRequest) request, + (RenderResponse) response); } else if (request instanceof ActionRequest) { applicationContext.firePortletActionRequest(application, - (ActionRequest) request, (ActionResponse) response); + window, (ActionRequest) request, + (ActionResponse) response); } else if (request instanceof EventRequest) { applicationContext.firePortletEventRequest(application, - (EventRequest) request, (EventResponse) response); + window, (EventRequest) request, + (EventResponse) response); } else if (request instanceof ResourceRequest) { applicationContext.firePortletResourceRequest(application, - (ResourceRequest) request, + window, (ResourceRequest) request, (ResourceResponse) response); } @@ -412,7 +438,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet // Handles AJAX UIDL requests applicationManager.handleUidlRequest( (ResourceRequest) request, - (ResourceResponse) response, this); + (ResourceResponse) response, this, window); return; } else { /* @@ -423,45 +449,9 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet return; } - /* - * Always use the main window when running inside a portlet. - */ - Window window = getPortletWindow(request, application); - if (window == null) { - throw new PortletException(ERROR_NO_WINDOW_FOUND); - } - - /* - * Sets terminal type for the window, if not already set - */ - if (window.getTerminal() == null) { - window.setTerminal(applicationContext.getBrowser()); - } - - /* - * Handle parameters - */ - final Map<String, String[]> parameters = request - .getParameterMap(); - if (window != null && parameters != null) { - window.handleParameters(parameters); - } - - if (requestType == RequestType.APPLICATION_RESOURCE) { - handleURI(applicationManager, window, - (ResourceRequest) request, - (ResourceResponse) response); - } else if (requestType == RequestType.RENDER) { - writeAjaxPage((RenderRequest) request, - (RenderResponse) response, window, application); - } else if (requestType == RequestType.EVENT) { - // nothing to do, listeners do all the work - } else if (requestType == RequestType.ACTION) { - // nothing to do, listeners do all the work - } else { - throw new IllegalStateException( - "handleRequest() without anything to do - should never happen!"); - } + handleOtherRequest(request, response, requestType, + application, window, applicationContext, + applicationManager); } } catch (final SessionExpiredException e) { // TODO Figure out a better way to deal with @@ -494,26 +484,67 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet } } + private void handleUnknownRequest(PortletRequest request, + PortletResponse response) { + System.err.println("Unknown request type"); + } + /** - * Returns a window for a portlet mode. To define custom content for a - * portlet mode, add (in the application) a window whose name matches the - * portlet mode name. By default, the main window is returned. + * Handle a portlet request that is not for static files, UIDL or upload. + * Also render requests are handled here. * - * Alternatively, a PortletListener can change the main window content. + * This method is called after starting the application and calling portlet + * and transaction listeners. * * @param request + * @param response + * @param requestType * @param application - * @return Window to show in the portlet for the given portlet mode + * @param applicationContext + * @param applicationManager + * @throws PortletException + * @throws IOException + * @throws MalformedURLException */ - protected Window getPortletWindow(PortletRequest request, - Application application) { - PortletMode mode = request.getPortletMode(); - Window window = application.getWindow(mode.toString()); - if (window != null) { - return window; + private void handleOtherRequest(PortletRequest request, + PortletResponse response, RequestType requestType, + Application application, Window window, + PortletApplicationContext2 applicationContext, + PortletCommunicationManager applicationManager) + throws PortletException, IOException, MalformedURLException { + if (window == null) { + throw new PortletException(ERROR_NO_WINDOW_FOUND); + } + + /* + * Sets terminal type for the window, if not already set + */ + if (window.getTerminal() == null) { + window.setTerminal(applicationContext.getBrowser()); + } + + /* + * Handle parameters + */ + final Map<String, String[]> parameters = request.getParameterMap(); + if (window != null && parameters != null) { + window.handleParameters(parameters); + } + + if (requestType == RequestType.APPLICATION_RESOURCE) { + handleURI(applicationManager, window, (ResourceRequest) request, + (ResourceResponse) response); + } else if (requestType == RequestType.RENDER) { + writeAjaxPage((RenderRequest) request, (RenderResponse) response, + window, application); + } else if (requestType == RequestType.EVENT) { + // nothing to do, listeners do all the work + } else if (requestType == RequestType.ACTION) { + // nothing to do, listeners do all the work + } else { + throw new IllegalStateException( + "handleRequest() without anything to do - should never happen!"); } - // no specific window found - return application.getMainWindow(); } private void updateBrowserProperties(WebBrowser browser, @@ -648,53 +679,26 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet handleRequest(request, response); } - /** - * Handles a request for the "view" (default) portlet mode. In Vaadin, the - * basic portlet modes ("view", "edit" and "help") are handled identically, - * and their behavior can be changed by registering windows in the - * application with window names identical to the portlet mode names. - * Alternatively, a PortletListener can change the application main window - * contents. - * - * To implement custom portlet modes, subclass the portlet class and - * implement a method annotated with {@link RenderMode} for the custom mode, - * calling {@link #handleRequest(PortletRequest, PortletResponse)} directly - * from it. - * - * Note that the portlet class in the portlet configuration needs to be - * changed when overriding methods of this class. - * - * @param request - * @param response - * @throws PortletException - * @throws IOException - */ @Override - protected void doView(RenderRequest request, RenderResponse response) + protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, IOException { - handleRequest(request, response); - } + try { + // try to let super handle - it'll call methods annotated for + // handling, the default doXYZ(), or throw if a handler for the mode + // is not found + super.doDispatch(request, response); - /** - * Handle a request for the "edit" portlet mode. - * - * @see #doView(RenderRequest, RenderResponse) - */ - @Override - protected void doEdit(RenderRequest request, RenderResponse response) - throws PortletException, IOException { - handleRequest(request, response); - } + } catch (PortletException e) { + if (e.getCause() == null) { + // No cause interpreted as 'unknown mode' - pass that trough + // so that the application can handle + handleRequest(request, response); - /** - * Handle a request for the "help" portlet mode. - * - * @see #doView(RenderRequest, RenderResponse) - */ - @Override - protected void doHelp(RenderRequest request, RenderResponse response) - throws PortletException, IOException { - handleRequest(request, response); + } else { + // Something else failed, pass on + throw e; + } + } } @Override @@ -833,17 +837,63 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet return null; } + /** + * Returns the URL from which the widgetset is served on the portal. + * + * @param widgetset + * @param request + * @return + */ protected String getWidgetsetURL(String widgetset, PortletRequest request) { return getStaticFilesLocation(request) + "/" + WIDGETSET_DIRECTORY_PATH + widgetset + "/" + widgetset + ".nocache.js?" + new Date().getTime(); } + /** + * Returns the theme URI for the named theme on the portal. + * + * Note that this is not the only location referring to the theme URI - also + * e.g. PortletCommunicationManager uses its own way to access the portlet + * 2.0 theme resources. + * + * @param themeName + * @param request + * @return + */ protected String getThemeURI(String themeName, PortletRequest request) { return getStaticFilesLocation(request) + "/" + THEME_DIRECTORY_PATH + themeName; } + /** + * Writes the html host page (aka kickstart page) that starts the actual + * Vaadin application. + * + * If one needs to override parts of the portlet HTML contents creation, it + * is suggested that one overrides one of several submethods including: + * <ul> + * <li> + * {@link #writeAjaxPageHtmlMainDiv(RenderRequest, RenderResponse, BufferedWriter, String)} + * <li> + * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)} + * <li> + * {@link #writeAjaxPageHtmlVaadinScripts(RenderRequest, RenderResponse, BufferedWriter, Application, String)} + * </ul> + * + * @param request + * the portlet request. + * @param response + * the portlet response to write to. + * @param window + * @param application + * @throws IOException + * if the writing failed due to input/output error. + * @throws MalformedURLException + * if the application is denied access the persistent data store + * represented by the given URL. + * @throws PortletException + */ protected void writeAjaxPage(RenderRequest request, RenderResponse response, Window window, Application application) throws IOException, MalformedURLException, PortletException { @@ -852,27 +902,62 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet final BufferedWriter page = new BufferedWriter(new OutputStreamWriter( response.getPortletOutputStream(), "UTF-8")); - // TODO check - String requestWidgetset = getApplicationOrSystemProperty( - PARAMETER_WIDGETSET, null); - String sharedWidgetset = getPortalProperty( - PORTAL_PARAMETER_VAADIN_WIDGETSET, request.getPortalContext()); - - String widgetset; - if (requestWidgetset != null) { - widgetset = requestWidgetset; - } else if (sharedWidgetset != null) { - widgetset = sharedWidgetset; - } else { - widgetset = DEFAULT_WIDGETSET; - } - // TODO Currently, we can only load widgetsets and themes from the // portal String themeName = getThemeForWindow(request, window); - String widgetsetURL = getWidgetsetURL(widgetset, request); + writeAjaxPageHtmlVaadinScripts(request, response, page, application, + themeName); + + /*- Add classnames; + * .v-app + * .v-app-loading + * .v-app-<simpleName for app class> + * .v-theme-<themeName, remove non-alphanum> + */ + String appClass = "v-app-"; + try { + appClass += getApplicationClass().getSimpleName(); + } catch (ClassNotFoundException e) { + appClass += "unknown"; + e.printStackTrace(); + } + String themeClass = "v-theme-" + + themeName.replaceAll("[^a-zA-Z0-9]", ""); + + String classNames = "v-app v-app-loading " + themeClass + " " + + appClass; + + String style = getApplicationProperty(PORTLET_PARAMETER_STYLE); + String divStyle = ""; + if (style != null) { + divStyle = "style=\"" + style + "\""; + } + + writeAjaxPageHtmlMainDiv(request, response, page, + request.getWindowID(), classNames, divStyle); + + page.close(); + } + + /** + * This method writes the scripts to load the widgetset and the themes as + * well as define Vaadin configuration parameters on the HTML fragment that + * starts the actual Vaadin application. + * + * @param request + * @param response + * @param writer + * @param application + * @param themeName + * @throws IOException + * @throws PortletException + */ + protected void writeAjaxPageHtmlVaadinScripts(RenderRequest request, + RenderResponse response, final BufferedWriter writer, + Application application, String themeName) throws IOException, + PortletException { String themeURI = getThemeURI(themeName, request); // fixed base theme to use - all portal pages with Vaadin @@ -880,52 +965,113 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet String portalTheme = getPortalProperty(PORTAL_PARAMETER_VAADIN_THEME, request.getPortalContext()); - // Get system messages - Application.SystemMessages systemMessages = null; - try { - systemMessages = getSystemMessages(); - } catch (SystemMessageException e) { - // failing to get the system messages is always a problem - throw new PortletException("Failed to obtain system messages!", e); - } - - page.write("<script type=\"text/javascript\">\n"); - page.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n " + writer.write("<script type=\"text/javascript\">\n"); + writer.write("if(!vaadin || !vaadin.vaadinConfigurations) {\n " + "if(!vaadin) { var vaadin = {}} \n" + "vaadin.vaadinConfigurations = {};\n" + "if (!vaadin.themesLoaded) { vaadin.themesLoaded = {}; }\n"); if (!isProductionMode()) { - page.write("vaadin.debug = true;\n"); + writer.write("vaadin.debug = true;\n"); } - page + + writeAjaxPageScriptWidgetset(request, response, writer); + + Map<String, String> config = getVaadinConfigurationMap(request, + response, application, themeURI); + writeAjaxPageScriptConfigurations(request, response, writer, config); + + writer.write("</script>\n"); + + writeAjaxPageHtmlTheme(request, writer, themeName, themeURI, + portalTheme); + + // TODO Warn if widgetset has not been loaded after 15 seconds + } + + /** + * Writes the script to load the widgetset on the HTML fragment created by + * the portlet. + * + * @param request + * @param response + * @param writer + * @throws IOException + */ + protected void writeAjaxPageScriptWidgetset(RenderRequest request, + RenderResponse response, final BufferedWriter writer) + throws IOException { + String requestWidgetset = getApplicationOrSystemProperty( + PARAMETER_WIDGETSET, null); + String sharedWidgetset = getPortalProperty( + PORTAL_PARAMETER_VAADIN_WIDGETSET, request.getPortalContext()); + + String widgetset; + if (requestWidgetset != null) { + widgetset = requestWidgetset; + } else if (sharedWidgetset != null) { + widgetset = sharedWidgetset; + } else { + widgetset = DEFAULT_WIDGETSET; + } + String widgetsetURL = getWidgetsetURL(widgetset, request); + writer .write("document.write('<iframe tabIndex=\"-1\" id=\"__gwt_historyFrame\" " + "style=\"width:0;height:0;border:0;overflow:" + "hidden\" src=\"javascript:false\"></iframe>');\n"); - page.write("document.write(\"<script language='javascript' src='" + writer.write("document.write(\"<script language='javascript' src='" + widgetsetURL + "'><\\/script>\");\n}\n"); + } - page.write("vaadin.vaadinConfigurations[\"" + request.getWindowID() - + "\"] = {"); + /** + * Returns the configuration parameters to pass to the client. + * + * To add configuration parameters for the client, override, call the super + * method and then modify the map. Overriding this method may also require + * client side changes in {@link ApplicationConnection} and + * {@link ApplicationConfiguration}. + * + * Note that this method must escape and quote the values when appropriate. + * + * The map returned is typically a {@link LinkedHashMap} to preserve + * insertion order, but it is not guaranteed to be one. + * + * @param request + * @param response + * @param application + * @param themeURI + * @return modifiable Map from parameter name to its full value + * @throws PortletException + */ + protected Map<String, String> getVaadinConfigurationMap( + RenderRequest request, RenderResponse response, + Application application, String themeURI) throws PortletException { + Map<String, String> config = new LinkedHashMap<String, String>(); /* * We need this in order to get uploads to work. */ PortletURL appUri = response.createActionURL(); - - page.write("appUri: '" + appUri.toString() + "', "); - page.write("usePortletURLs: true, "); - + config.put("appUri", "'" + appUri.toString() + "'"); + config.put("usePortletURLs", "true"); ResourceURL uidlUrlBase = response.createResourceURL(); uidlUrlBase.setResourceID("UIDL"); + config.put("portletUidlURLBase", "'" + uidlUrlBase.toString() + "'"); + config.put("pathInfo", "''"); + config.put("themeUri", "'" + themeURI + "'"); + + String versionInfo = "{vaadinVersion:\"" + + AbstractApplicationServlet.VERSION + + "\",applicationVersion:\"" + application.getVersion() + "\"}"; + config.put("versionInfo", versionInfo); - page.write("portletUidlURLBase: '" + uidlUrlBase.toString() + "', "); - page.write("pathInfo: '', "); - page.write("themeUri: '" + themeURI + "', "); - page.write("versionInfo : {vaadinVersion:\""); - page.write(AbstractApplicationServlet.VERSION); - page.write("\",applicationVersion:\""); - page.write(application.getVersion()); - page.write("\"},"); + // Get system messages + Application.SystemMessages systemMessages = null; + try { + systemMessages = getSystemMessages(); + } catch (SystemMessageException e) { + // failing to get the system messages is always a problem + throw new PortletException("Failed to obtain system messages!", e); + } if (systemMessages != null) { // Write the CommunicationError -message to client String caption = systemMessages.getCommunicationErrorCaption(); @@ -941,73 +1087,117 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet url = "\"" + url + "\""; } - page.write("\"comErrMsg\": {" + "\"caption\":" + caption + "," + config.put("\"comErrMsg\"", "{" + "\"caption\":" + caption + "," + "\"message\" : " + message + "," + "\"url\" : " + url + "}"); } - page.write("};\n</script>\n"); - page.write("<script type=\"text/javascript\">\n"); + return config; + } + + /** + * Constructs the Vaadin configuration section for + * {@link ApplicationConnection} and {@link ApplicationConfiguration}. + * + * Typically this method should not be overridden. Instead, modify + * {@link #getVaadinConfigurationMap(RenderRequest, RenderResponse, Application, String)} + * . + * + * @param request + * @param response + * @param writer + * @param config + * @throws IOException + * @throws PortletException + */ + protected void writeAjaxPageScriptConfigurations(RenderRequest request, + RenderResponse response, final BufferedWriter writer, + Map<String, String> config) throws IOException, PortletException { + + writer.write("vaadin.vaadinConfigurations[\"" + request.getWindowID() + + "\"] = {"); + + Iterator<String> keyIt = config.keySet().iterator(); + while (keyIt.hasNext()) { + String key = keyIt.next(); + writer.write(key + ": " + config.get(key)); + if (keyIt.hasNext()) { + writer.write(", "); + } + } + + writer.write("};\n"); + } + + /** + * Writes the Vaadin theme loading section of the portlet HTML. Loads both + * the portal theme and the portlet theme in this order, skipping loading of + * themes that are already loaded (matched by name). + * + * @param request + * @param writer + * @param themeName + * @param themeURI + * @param portalTheme + * @throws IOException + */ + protected void writeAjaxPageHtmlTheme(RenderRequest request, + final BufferedWriter writer, String themeName, String themeURI, + String portalTheme) throws IOException { + writer.write("<script type=\"text/javascript\">\n"); if (portalTheme == null) { portalTheme = DEFAULT_THEME_NAME; } - page.write("if(!vaadin.themesLoaded['" + portalTheme + "']) {\n"); - page.write("var defaultStylesheet = document.createElement('link');\n"); - page.write("defaultStylesheet.setAttribute('rel', 'stylesheet');\n"); - page.write("defaultStylesheet.setAttribute('type', 'text/css');\n"); - page.write("defaultStylesheet.setAttribute('href', '" + writer.write("if(!vaadin.themesLoaded['" + portalTheme + "']) {\n"); + writer + .write("var defaultStylesheet = document.createElement('link');\n"); + writer.write("defaultStylesheet.setAttribute('rel', 'stylesheet');\n"); + writer.write("defaultStylesheet.setAttribute('type', 'text/css');\n"); + writer.write("defaultStylesheet.setAttribute('href', '" + getThemeURI(portalTheme, request) + "/styles.css');\n"); - page + writer .write("document.getElementsByTagName('head')[0].appendChild(defaultStylesheet);\n"); - page.write("vaadin.themesLoaded['" + portalTheme + "'] = true;\n}\n"); + writer.write("vaadin.themesLoaded['" + portalTheme + "'] = true;\n}\n"); if (!portalTheme.equals(themeName)) { - page.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n"); - page.write("var stylesheet = document.createElement('link');\n"); - page.write("stylesheet.setAttribute('rel', 'stylesheet');\n"); - page.write("stylesheet.setAttribute('type', 'text/css');\n"); - page.write("stylesheet.setAttribute('href', '" + themeURI + writer.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n"); + writer.write("var stylesheet = document.createElement('link');\n"); + writer.write("stylesheet.setAttribute('rel', 'stylesheet');\n"); + writer.write("stylesheet.setAttribute('type', 'text/css');\n"); + writer.write("stylesheet.setAttribute('href', '" + themeURI + "/styles.css');\n"); - page + writer .write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n"); - page.write("vaadin.themesLoaded['" + themeName + "'] = true;\n}\n"); - } - - page.write("</script>\n"); - - // TODO Warn if widgetset has not been loaded after 15 seconds - - /*- Add classnames; - * .v-app - * .v-app-loading - * .v-app-<simpleName for app class> - * .v-theme-<themeName, remove non-alphanum> - */ - - String appClass = "v-app-"; - try { - appClass += getApplicationClass().getSimpleName(); - } catch (ClassNotFoundException e) { - appClass += "unknown"; - e.printStackTrace(); + writer.write("vaadin.themesLoaded['" + themeName + + "'] = true;\n}\n"); } - String themeClass = "v-theme-" - + themeName.replaceAll("[^a-zA-Z0-9]", ""); - String classNames = "v-app v-app-loading " + themeClass + " " - + appClass; + writer.write("</script>\n"); + } - String style = getApplicationProperty(PORTLET_PARAMETER_STYLE); - String divStyle = ""; - if (style != null) { - divStyle = "style=\"" + style + "\""; - } - page.write("<div id=\"" + request.getWindowID() + "\" class=\"" + /** + * Method to write the div element into which that actual Vaadin application + * is rendered. + * <p> + * Override this method if you want to add some custom html around around + * the div element into which the actual Vaadin application will be + * rendered. + * + * @param request + * @param response + * @param writer + * @param id + * @param classNames + * @param divStyle + * @throws IOException + */ + protected void writeAjaxPageHtmlMainDiv(RenderRequest request, + RenderResponse response, final BufferedWriter writer, String id, + String classNames, String divStyle) throws IOException { + writer.write("<div id=\"" + id + "\" class=\"" + classNames + "\" " + divStyle + "></div>\n"); - - page.close(); } /** @@ -1017,7 +1207,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet * @param window * @return */ - private String getThemeForWindow(PortletRequest request, Window window) { + protected String getThemeForWindow(PortletRequest request, Window window) { // Finds theme name String themeName; @@ -1189,12 +1379,21 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet + "\"appError\": {" + "\"caption\":" + caption + "," + "\"message\" : " + message + "," + "\"url\" : " + url + "}}, \"resources\": {}, \"locales\":[]}]"); - outWriter.flush(); outWriter.close(); - out.flush(); } - private static String getPortalProperty(String name, PortalContext context) { + /** + * Returns a portal configuration property. + * + * Liferay is handled separately as + * {@link PortalContext#getProperty(String)} does not return portal + * properties from e.g. portal-ext.properties . + * + * @param name + * @param context + * @return + */ + protected static String getPortalProperty(String name, PortalContext context) { boolean isLifeRay = context.getPortalInfo().toLowerCase().contains( "liferay"); diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index 7a3978f323..8c901ba7e7 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -362,6 +362,9 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements HttpServletResponse response) throws ServletException, IOException { RequestType requestType = getRequestType(request); + if (!ensureCookiesEnabled(requestType, request, response)) { + return; + } if (requestType == RequestType.STATIC_FILE) { serveStaticResources(request, response); @@ -435,7 +438,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements return; } else if (requestType == RequestType.UIDL) { // Handles AJAX UIDL requests - applicationManager.handleUidlRequest(request, response, this); + Window window = applicationManager.getApplicationWindow( + request, this, application, null); + applicationManager.handleUidlRequest(request, response, this, + window); return; } @@ -502,6 +508,39 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } } + /** + * Check that cookie support is enabled in the browser. Only checks UIDL + * requests. + * + * @param requestType + * Type of the request as returned by + * {@link #getRequestType(HttpServletRequest)} + * @param request + * The request from the browser + * @param response + * The response to which an error can be written + * @return false if cookies are disabled, true otherwise + * @throws IOException + */ + private boolean ensureCookiesEnabled(RequestType requestType, + HttpServletRequest request, HttpServletResponse response) + throws IOException { + if (requestType == RequestType.UIDL && !isRepaintAll(request)) { + // In all other but the first UIDL request a cookie should be + // returned by the browser. + // This can be removed if cookieless mode (#3228) is supported + if (request.getRequestedSessionId() == null) { + // User has cookies disabled + criticalNotification(request, response, getSystemMessages() + .getCookiesDisabledCaption(), getSystemMessages() + .getCookiesDisabledMessage(), null, getSystemMessages() + .getCookiesDisabledURL()); + return false; + } + } + return true; + } + private void updateBrowserProperties(WebBrowser browser, HttpServletRequest request) { browser.updateBrowserProperties(request.getLocale(), request @@ -535,38 +574,40 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } /** - * Send notification to client's application. Used to notify client of - * critical errors and session expiration due to long inactivity. Server has - * no knowledge of what application client refers to. + * Send a notification to client's application. Used to notify client of + * critical errors, session expiration and more. Server has no knowledge of + * what application client refers to. * * @param request * the HTTP request instance. * @param response * the HTTP response to write to. * @param caption - * for the notification + * the notification caption * @param message - * for the notification + * to notification body * @param details - * a detail message to show in addition to the passed message. - * Currently shown directly but could be hidden behind a details - * drop down. + * a detail message to show in addition to the message. Currently + * shown directly below the message but could be hidden behind a + * details drop down in the future. Mainly used to give + * additional information not necessarily useful to the end user. * @param url - * url to load after message, null for current page + * url to load when the message is dismissed. Null will reload + * the current page. * @throws IOException * if the writing failed due to input/output error. */ - void criticalNotification(HttpServletRequest request, + protected final void criticalNotification(HttpServletRequest request, HttpServletResponse response, String caption, String message, String details, String url) throws IOException { - // clients JS app is still running, but server application either - // no longer exists or it might fail to perform reasonably. - // send a notification to client's application and link how - // to "restart" application. + if (!isUIDLRequest(request)) { + throw new RuntimeException( + "criticalNotification can only be used in UIDL requests"); + } if (caption != null) { - caption = "\"" + caption + "\""; + caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\""; } if (details != null) { if (message == null) { @@ -575,11 +616,12 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements message += "<br/><br/>" + details; } } + if (message != null) { - message = "\"" + message + "\""; + message = "\"" + JsonPaintTarget.escapeJSON(message) + "\""; } if (url != null) { - url = "\"" + url + "\""; + url = "\"" + JsonPaintTarget.escapeJSON(url) + "\""; } // Set the response type @@ -1489,7 +1531,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements * is rendered. * <p> * Override this method if you want to add some custom html around around - * the div element into which the actual vaadin application will be + * the div element into which the actual Vaadin application will be * rendered. * * @param page @@ -1507,8 +1549,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements } /** - * - * * Method to write the script part of the page which loads needed vaadin + * Method to write the script part of the page which loads needed Vaadin * scripts and themes. * <p> * Override this method if you want to add some custom html around scripts. diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 67b806f0f0..481e42c0e9 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -498,11 +498,14 @@ public abstract class AbstractCommunicationManager implements * @param request * @param response * @param callback + * @param window + * target window for the UIDL request, can be null if target not + * found * @throws IOException * @throws InvalidUIDLSecurityKeyException */ protected void doHandleUidlRequest(Request request, Response response, - Callback callback) throws IOException, + Callback callback, Window window) throws IOException, InvalidUIDLSecurityKeyException { // repaint requested or session has timed out and new one is created @@ -528,10 +531,7 @@ public abstract class AbstractCommunicationManager implements synchronized (application) { // Finds the window within the application - Window window = null; if (application.isRunning()) { - window = doGetApplicationWindow(request, callback, application, - null); // Returns if no window found if (window == null) { // This should not happen, no windows exists but @@ -588,8 +588,7 @@ public abstract class AbstractCommunicationManager implements } } - // out.flush(); - this line will cause errors when deployed on GateIn. - out.close(); + outWriter.close(); } /** @@ -947,7 +946,6 @@ public abstract class AbstractCommunicationManager implements outWriter.print("}]"); } - outWriter.flush(); outWriter.close(); } @@ -1871,7 +1869,7 @@ public abstract class AbstractCommunicationManager implements } return stream; } else { - // Resolve the prefix end inded + // Resolve the prefix end index final int index = uri.indexOf('/'); if (index > 0) { String prefix = uri.substring(0, index); diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java index 8799e37899..4e6bfbd158 100644 --- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java @@ -238,16 +238,22 @@ public class CommunicationManager extends AbstractCommunicationManager { * * @param request * @param response + * @param applicationServlet + * @param window + * target window of the UIDL request, can be null if window not + * found * @throws IOException * @throws ServletException */ public void handleUidlRequest(HttpServletRequest request, HttpServletResponse response, - AbstractApplicationServlet applicationServlet) throws IOException, - ServletException, InvalidUIDLSecurityKeyException { + AbstractApplicationServlet applicationServlet, Window window) + throws IOException, ServletException, + InvalidUIDLSecurityKeyException { doHandleUidlRequest(new HttpServletRequestWrapper(request), new HttpServletResponseWrapper(response), - new AbstractApplicationServletWrapper(applicationServlet)); + new AbstractApplicationServletWrapper(applicationServlet), + window); } /** diff --git a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java index ab1b1da81f..a6f8d7f204 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java +++ b/src/com/vaadin/terminal/gwt/server/PortletApplicationContext2.java @@ -139,18 +139,18 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext { } } - public void firePortletRenderRequest(Application app, + public void firePortletRenderRequest(Application app, Window window, RenderRequest request, RenderResponse response) { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { l.handleRenderRequest(request, new RestrictedRenderResponse( - response)); + response), window); } } } - public void firePortletActionRequest(Application app, + public void firePortletActionRequest(Application app, Window window, ActionRequest request, ActionResponse response) { String key = request.getParameter(ActionRequest.ACTION_NAME); if (eventActionDestinationMap.containsKey(key)) { @@ -172,28 +172,28 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { - l.handleActionRequest(request, response); + l.handleActionRequest(request, response, window); } } } } - public void firePortletEventRequest(Application app, EventRequest request, - EventResponse response) { + public void firePortletEventRequest(Application app, Window window, + EventRequest request, EventResponse response) { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { - l.handleEventRequest(request, response); + l.handleEventRequest(request, response, window); } } } - public void firePortletResourceRequest(Application app, + public void firePortletResourceRequest(Application app, Window window, ResourceRequest request, ResourceResponse response) { Set<PortletListener> listeners = portletListeners.get(app); if (listeners != null) { for (PortletListener l : listeners) { - l.handleResourceRequest(request, response); + l.handleResourceRequest(request, response, window); } } } @@ -201,16 +201,16 @@ public class PortletApplicationContext2 extends AbstractWebApplicationContext { public interface PortletListener extends Serializable { public void handleRenderRequest(RenderRequest request, - RenderResponse response); + RenderResponse response, Window window); public void handleActionRequest(ActionRequest request, - ActionResponse response); + ActionResponse response, Window window); public void handleEventRequest(EventRequest request, - EventResponse response); + EventResponse response, Window window); public void handleResourceRequest(ResourceRequest request, - ResourceResponse response); + ResourceResponse response, Window window); } /** diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java index 3a5cd90f7c..7fe324b1db 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java @@ -3,6 +3,7 @@ package com.vaadin.terminal.gwt.server; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Method; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; @@ -13,6 +14,8 @@ import javax.portlet.PortletResponse; import javax.portlet.PortletSession; import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequestWrapper; import com.vaadin.Application; import com.vaadin.external.org.apache.commons.fileupload.FileItemIterator; @@ -54,7 +57,20 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { } public String getParameter(String name) { - return request.getParameter(name); + String value = request.getParameter(name); + if (value == null) { + // for GateIn portlet container simple-portal + try { + Method getRealReq = request.getClass().getMethod( + "getRealRequest"); + HttpServletRequestWrapper origRequest = (HttpServletRequestWrapper) getRealReq + .invoke(request); + value = origRequest.getParameter(name); + } catch (Exception e) { + // do nothing - not on GateIn simple-portal + } + } + return value; } public String getRequestID() { @@ -165,19 +181,6 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { + themeName + "/" + resource); } - /** - * Find the application window to use based on the portlet mode. For - * internal use only, not in the {@link Callback} interface. - * - * @param request - * @param application - * @return - */ - public Window getPortletWindow(PortletRequest request, - Application application) { - return portlet.getPortletWindow(request, application); - } - } public PortletCommunicationManager(Application application) { @@ -222,11 +225,12 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { public void handleUidlRequest(ResourceRequest request, ResourceResponse response, - AbstractApplicationPortlet applicationPortlet) + AbstractApplicationPortlet applicationPortlet, Window window) throws InvalidUIDLSecurityKeyException, IOException { doHandleUidlRequest(new PortletRequestWrapper(request), new PortletResponseWrapper(response), - new AbstractApplicationPortletWrapper(applicationPortlet)); + new AbstractApplicationPortletWrapper(applicationPortlet), + window); } DownloadStream handleURI(Window window, ResourceRequest request, @@ -237,18 +241,29 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { new AbstractApplicationPortletWrapper(applicationPortlet)); } - @Override - protected Window doGetApplicationWindow(Request request, Callback callback, + /** + * Gets the existing application or creates a new one. Get a window within + * an application based on the requested URI. + * + * @param request + * the portlet Request. + * @param applicationPortlet + * @param application + * the Application to query for window. + * @param assumedWindow + * if the window has been already resolved once, this parameter + * must contain the window. + * @return Window matching the given URI or null if not found. + * @throws ServletException + * if an exception has occurred that interferes with the + * servlet's normal operation. + */ + Window getApplicationWindow(PortletRequest request, + AbstractApplicationPortlet applicationPortlet, Application application, Window assumedWindow) { - // find window based on portlet mode - if (assumedWindow == null - && callback instanceof AbstractApplicationPortletWrapper - && request.getWrappedRequest() instanceof PortletRequest) { - assumedWindow = ((AbstractApplicationPortletWrapper) callback) - .getPortletWindow((PortletRequest) request - .getWrappedRequest(), application); - } - return super.doGetApplicationWindow(request, callback, application, - assumedWindow); + return doGetApplicationWindow(new PortletRequestWrapper(request), + new AbstractApplicationPortletWrapper(applicationPortlet), + application, assumedWindow); } + } diff --git a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java index b3c670ba81..ca20b8b895 100644 --- a/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java +++ b/src/com/vaadin/terminal/gwt/widgetsetutils/ClassPathExplorer.java @@ -19,7 +19,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -175,7 +174,9 @@ public class ClassPathExplorer { for (int i = 0; i < widgetsetNames.length; i++) { String widgetsetname = widgetsetNames[i].trim() .intern(); - widgetsets.put(widgetsetname, location); + if (!widgetsetname.equals("")) { + widgetsets.put(widgetsetname, location); + } } } } @@ -439,8 +440,15 @@ public class ClassPathExplorer { * @return URL */ public static URL getDefaultSourceDirectory() { - logger.fine("classpathLocations keys: " - + new TreeSet<URL>(classpathLocations.keySet())); + if (logger.isLoggable(Level.FINE)) { + logger.fine("classpathLocations keys:"); + ArrayList<URL> locations = new ArrayList<URL>(classpathLocations + .keySet()); + for (URL location : locations) { + logger.fine(location.toString()); + } + } + Iterator<String> it = rawClasspathEntries.iterator(); while (it.hasNext()) { String entry = it.next(); |