]> source.dussan.org Git - vaadin-framework.git/commitdiff
Update theme resource references for legacy components (#17027)
authorArtur Signell <artur@vaadin.com>
Wed, 24 Jun 2015 18:03:26 +0000 (21:03 +0300)
committerVaadin Code Review <review@vaadin.com>
Fri, 26 Jun 2015 05:25:00 +0000 (05:25 +0000)
Change-Id: Id4f119b22d44f6abf63e730442e22a34e7c1953f

client/src/com/vaadin/client/ui/VFilterSelect.java
client/src/com/vaadin/client/ui/VScrollTable.java
client/src/com/vaadin/client/ui/ui/UIConnector.java
uitest/src/com/vaadin/tests/themes/LegacyComponentThemeChange.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/themes/LegacyComponentThemeChangeTest.java [new file with mode: 0644]

index d99779b7ec1373caa7d46a84a213136341c76552..7951759fa21075cb9d9aafbca6f3cdc711baf092 100644 (file)
@@ -79,6 +79,7 @@ import com.vaadin.shared.AbstractComponentState;
 import com.vaadin.shared.EventId;
 import com.vaadin.shared.ui.ComponentStateUtil;
 import com.vaadin.shared.ui.combobox.FilteringMode;
+import com.vaadin.shared.util.SharedUtil;
 
 /**
  * Client side implementation of the Select component.
@@ -98,7 +99,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
 
         private final String key;
         private final String caption;
-        private String iconUri;
+        private String untranslatedIconUri;
 
         /**
          * Constructor
@@ -110,8 +111,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
             key = uidl.getStringAttribute("key");
             caption = uidl.getStringAttribute("caption");
             if (uidl.hasAttribute("icon")) {
-                iconUri = client.translateVaadinUri(uidl
-                        .getStringAttribute("icon"));
+                untranslatedIconUri = uidl.getStringAttribute("icon");
             }
         }
 
@@ -124,7 +124,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
         @Override
         public String getDisplayString() {
             final StringBuffer sb = new StringBuffer();
-            final Icon icon = client.getIcon(iconUri);
+            final Icon icon = client.getIcon(client
+                    .translateVaadinUri(untranslatedIconUri));
             if (icon != null) {
                 sb.append(icon.getElement().getString());
             }
@@ -164,7 +165,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
          * @return
          */
         public String getIconUri() {
-            return iconUri;
+            return client.translateVaadinUri(untranslatedIconUri);
         }
 
         /**
@@ -190,8 +191,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
                     || (caption != null && !caption.equals(other.caption))) {
                 return false;
             }
-            if ((iconUri == null && other.iconUri != null)
-                    || (iconUri != null && !iconUri.equals(other.iconUri))) {
+            if (!SharedUtil.equals(untranslatedIconUri,
+                    other.untranslatedIconUri)) {
                 return false;
             }
             return true;
index 0ef0cb6949b9a6a93822ad7d1d5c11a48cbfe063..6bb3199b086559bd78f3b3c399342b9bece80844 100644 (file)
@@ -1647,8 +1647,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
             actionMap.put(key + "_c", caption);
             if (action.hasAttribute("icon")) {
                 // TODO need some uri handling ??
-                actionMap.put(key + "_i", client.translateVaadinUri(action
-                        .getStringAttribute("icon")));
+                actionMap.put(key + "_i", action.getStringAttribute("icon"));
             } else {
                 actionMap.remove(key + "_i");
             }
@@ -1661,7 +1660,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets,
     }
 
     public String getActionIcon(String actionKey) {
-        return actionMap.get(actionKey + "_i");
+        return client.translateVaadinUri(actionMap.get(actionKey + "_i"));
     }
 
     private void updateHeader(String[] strings) {
index 264b2de0e192ae819b844e6aa23e5fe9c2041326..cfb444ada6cb7b793946cc8d5f8e7ae29f83360d 100644 (file)
@@ -1005,6 +1005,8 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
                     activeTheme);
         }
 
+        String oldThemeBase = getConnection().translateVaadinUri("theme://");
+
         activeTheme = newTheme;
 
         if (newTheme != null) {
@@ -1013,12 +1015,61 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
                     activeTheme);
 
             updateVaadinFavicon(newTheme);
+
         }
 
         forceStateChangeRecursively(UIConnector.this);
+        // UIDL has no stored URL which we can repaint so we do some find and
+        // replace magic...
+        String newThemeBase = getConnection().translateVaadinUri("theme://");
+        replaceThemeAttribute(oldThemeBase, newThemeBase);
+
         getLayoutManager().forceLayout();
     }
 
+    /**
+     * Finds all attributes where theme:// urls have possibly been used and
+     * replaces any old theme url with a new one
+     * 
+     * @param oldPrefix
+     *            The start of the old theme URL
+     * @param newPrefix
+     *            The start of the new theme URL
+     */
+    private void replaceThemeAttribute(String oldPrefix, String newPrefix) {
+        // Images
+        replaceThemeAttribute("src", oldPrefix, newPrefix);
+        // Embedded flash
+        replaceThemeAttribute("value", oldPrefix, newPrefix);
+        replaceThemeAttribute("movie", oldPrefix, newPrefix);
+    }
+
+    /**
+     * Finds any attribute of the given type where theme:// urls have possibly
+     * been used and replaces any old theme url with a new one
+     * 
+     * @param attributeName
+     *            The name of the attribute, e.g. "src"
+     * @param oldPrefix
+     *            The start of the old theme URL
+     * @param newPrefix
+     *            The start of the new theme URL
+     */
+    private void replaceThemeAttribute(String attributeName, String oldPrefix,
+            String newPrefix) {
+        // Find all "attributeName=" which start with "oldPrefix" using e.g.
+        // [^src='http://oldpath']
+        NodeList<Element> elements = querySelectorAll("[" + attributeName
+                + "^='" + oldPrefix + "']");
+        for (int i = 0; i < elements.getLength(); i++) {
+            Element element = elements.getItem(i);
+            element.setAttribute(
+                    attributeName,
+                    element.getAttribute(attributeName).replace(oldPrefix,
+                            newPrefix));
+        }
+    }
+
     /**
      * Force a full recursive recheck of every connector's state variables.
      * 
diff --git a/uitest/src/com/vaadin/tests/themes/LegacyComponentThemeChange.java b/uitest/src/com/vaadin/tests/themes/LegacyComponentThemeChange.java
new file mode 100644 (file)
index 0000000..4582123
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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.themes;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.event.Action;
+import com.vaadin.event.Action.Handler;
+import com.vaadin.server.ThemeResource;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.util.PersonContainer;
+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.Embedded;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.MenuBar;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.VerticalLayout;
+
+@Theme("reindeer")
+public class LegacyComponentThemeChange extends AbstractTestUIWithLog {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        VerticalLayout vl = new VerticalLayout();
+        vl.setCaption("Change theme by clicking a button");
+        HorizontalLayout hl = new HorizontalLayout();
+        for (final String theme : new String[] { "reindeer", "runo" }) {
+            Button b = new Button(theme);
+            b.setId(theme + "");
+            b.addClickListener(new ClickListener() {
+
+                @Override
+                public void buttonClick(ClickEvent event) {
+                    getUI().setTheme(theme);
+                }
+            });
+            hl.addComponent(b);
+        }
+        vl.addComponent(hl);
+
+        // Always wants to use icon from Runo, even if we change theme
+        ThemeResource alwaysTheSameIconImage = new ThemeResource(
+                "../runo/icons/16/ok.png");
+        ThemeResource varyingIcon = new ThemeResource("menubar-theme-icon.png");
+        MenuBar bar = new MenuBar();
+        bar.addItem("runo", alwaysTheSameIconImage, null);
+        bar.addItem("seletedtheme", varyingIcon, null);
+
+        vl.addComponent(bar);
+
+        ComboBox cb = new ComboBox("ComboBox");
+        cb.addItem("No icon");
+        cb.addItem("Icon");
+        cb.setItemIcon("Icon", new ThemeResource("comboboxicon.png"));
+        cb.setValue("Icon");
+
+        vl.addComponent(cb);
+
+        Embedded e = new Embedded("embedded");
+        e.setMimeType("application/x-shockwave-flash");
+        e.setType(Embedded.TYPE_OBJECT);
+        e.setSource(new ThemeResource("embedded.src"));
+        vl.addComponent(e);
+
+        Table t = new Table();
+        t.addActionHandler(new Handler() {
+            @Override
+            public void handleAction(Action action, Object sender, Object target) {
+            }
+
+            @Override
+            public Action[] getActions(Object target, Object sender) {
+                return new Action[] { new Action("Theme icon",
+                        new ThemeResource("action-icon.png")) };
+            }
+        });
+        PersonContainer pc = PersonContainer.createWithTestData();
+        pc.addNestedContainerBean("address");
+        t.setContainerDataSource(pc);
+        vl.addComponent(t);
+
+        addComponent(vl);
+    }
+
+}
diff --git a/uitest/src/com/vaadin/tests/themes/LegacyComponentThemeChangeTest.java b/uitest/src/com/vaadin/tests/themes/LegacyComponentThemeChangeTest.java
new file mode 100644 (file)
index 0000000..c659310
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * 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.themes;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elements.ComboBoxElement;
+import com.vaadin.testbench.elements.EmbeddedElement;
+import com.vaadin.testbench.elements.MenuBarElement;
+import com.vaadin.testbench.elements.TableElement;
+import com.vaadin.testbench.parallel.Browser;
+import com.vaadin.testbench.parallel.BrowserUtil;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+public class LegacyComponentThemeChangeTest extends MultiBrowserTest {
+
+    @Override
+    public List<DesiredCapabilities> getBrowsersToTest() {
+        // Seems like stylesheet onload is not fired on PhantomJS
+        // https://github.com/ariya/phantomjs/issues/12332
+        List<DesiredCapabilities> l = getBrowsersExcludingPhantomJS();
+
+        // For some reason, IE times out when trying to open the combobox,
+        // #18341
+        l.remove(Browser.IE11.getDesiredCapabilities());
+        return l;
+    }
+
+    @Test
+    public void legacyComponentThemeResourceChange() {
+        openTestURL();
+        String theme = "reindeer";
+        assertMenubarTheme(theme);
+        assertCombobBoxTheme(theme);
+        assertTableTheme(theme);
+        assertEmbeddedTheme(theme);
+
+        theme = "runo";
+        changeTheme(theme);
+        assertMenubarTheme(theme);
+        assertCombobBoxTheme(theme);
+        assertTableTheme(theme);
+        assertEmbeddedTheme(theme);
+
+        theme = "reindeer";
+        changeTheme(theme);
+        assertMenubarTheme(theme);
+        assertCombobBoxTheme(theme);
+        assertTableTheme(theme);
+        assertEmbeddedTheme(theme);
+
+    }
+
+    private void assertEmbeddedTheme(String theme) {
+        if (BrowserUtil.isIE8(getDesiredCapabilities())) {
+            // IE8 won't initialize the dummy flash properly
+            return;
+        }
+        EmbeddedElement e = $(EmbeddedElement.class).first();
+        WebElement movieParam = e.findElement(By
+                .xpath(".//param[@name='movie']"));
+        WebElement embed = e.findElement(By.xpath(".//embed"));
+        assertAttributePrefix(movieParam, "value", theme);
+        assertAttributePrefix(embed, "src", theme);
+        assertAttributePrefix(embed, "movie", theme);
+    }
+
+    private void assertTableTheme(String theme) {
+        TableElement t = $(TableElement.class).first();
+        t.getRow(0).contextClick();
+        WebElement popup = findElement(By.className("v-contextmenu"));
+
+        WebElement actionImage = popup.findElement(By.xpath(".//img"));
+        assertAttributePrefix(actionImage, "src", theme);
+    }
+
+    private void assertCombobBoxTheme(String theme) {
+        ComboBoxElement cb = $(ComboBoxElement.class).first();
+        WebElement selectedImage = cb.findElement(By.xpath("./img"));
+        assertAttributePrefix(selectedImage, "src", theme);
+
+        cb.openPopup();
+        WebElement popup = findElement(By
+                .className("v-filterselect-suggestpopup"));
+        WebElement itemImage = popup.findElement(By.xpath(".//img"));
+        assertAttributePrefix(itemImage, "src", theme);
+    }
+
+    private void assertMenubarTheme(String theme) {
+        // The runoImage must always come from Runo
+        WebElement runoImage = $(MenuBarElement.class).first().findElement(
+                By.xpath(".//span[text()='runo']/img"));
+        String runoImageSrc = runoImage.getAttribute("src");
+
+        // Something in Selenium normalizes the image so it becomes
+        // "/themes/runo/icons/16/ok.png" here although it is
+        // "/themes/<currenttheme>/../runo/icons/16/ok.png" in the browser
+        Assert.assertEquals(getThemeURL("runo") + "icons/16/ok.png",
+                runoImageSrc);
+
+        // The other image should change with the theme
+        WebElement themeImage = $(MenuBarElement.class).first().findElement(
+                By.xpath(".//span[text()='seletedtheme']/img"));
+        assertAttributePrefix(themeImage, "src", theme);
+    }
+
+    private void assertAttributePrefix(WebElement element, String attribute,
+            String theme) {
+        String value = element.getAttribute(attribute);
+        String expectedPrefix = getThemeURL(theme);
+        Assert.assertTrue("Attribute " + attribute + "='" + value
+                + "' does not start with " + expectedPrefix,
+                value.startsWith(expectedPrefix));
+
+    }
+
+    private String getThemeURL(String theme) {
+        return getBaseURL() + "/VAADIN/themes/" + theme + "/";
+    }
+
+    private void changeTheme(String theme) {
+        $(ButtonElement.class).id(theme).click();
+        waitForThemeToChange(theme);
+    }
+
+    private void waitForThemeToChange(final String theme) {
+
+        final WebElement rootDiv = findElement(By
+                .xpath("//div[contains(@class,'v-app')]"));
+        waitUntil(new ExpectedCondition<Boolean>() {
+
+            @Override
+            public Boolean apply(WebDriver input) {
+                String rootClass = rootDiv.getAttribute("class").trim();
+
+                return rootClass.contains(theme);
+            }
+        }, 30);
+    }
+
+}