]> source.dussan.org Git - vaadin-framework.git/commitdiff
Initial javascript callback support (#6730)
authorLeif Åstrand <leif@vaadin.com>
Mon, 11 Jun 2012 18:52:26 +0000 (21:52 +0300)
committerLeif Åstrand <leif@vaadin.com>
Mon, 11 Jun 2012 18:52:26 +0000 (21:52 +0300)
src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java [new file with mode: 0644]
src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java [new file with mode: 0644]
src/com/vaadin/terminal/gwt/server/JsonCodec.java
src/com/vaadin/ui/JavascriptCallback.java [new file with mode: 0644]
src/com/vaadin/ui/JavascriptManager.java [new file with mode: 0644]
src/com/vaadin/ui/Root.java
tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.html [new file with mode: 0644]
tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.java [new file with mode: 0644]

diff --git a/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java
new file mode 100644 (file)
index 0000000..979a6d2
--- /dev/null
@@ -0,0 +1,78 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions.javascriptmanager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.json.client.JSONArray;
+import com.vaadin.terminal.gwt.client.communication.MethodInvocation;
+import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
+import com.vaadin.terminal.gwt.client.ui.AbstractConnector;
+import com.vaadin.terminal.gwt.client.ui.Connect;
+import com.vaadin.ui.JavascriptManager;
+
+@Connect(JavascriptManager.class)
+public class JavascriptManagerConnector extends AbstractConnector {
+    private Set<String> currentNames = new HashSet<String>();
+
+    @Override
+    public void onStateChanged(StateChangeEvent stateChangeEvent) {
+        super.onStateChanged(stateChangeEvent);
+
+        Set<String> newNames = getState().getNames();
+
+        // Current names now only contains orphan callbacks
+        currentNames.removeAll(newNames);
+
+        for (String name : currentNames) {
+            removeCallback(name);
+        }
+
+        currentNames = new HashSet<String>(newNames);
+        for (String name : newNames) {
+            addCallback(name);
+        }
+    }
+
+    // TODO Ensure we don't overwrite anything (important) in $wnd
+    private native void addCallback(String name)
+    /*-{
+        var m = this;
+        $wnd[name] = $entry(function() {
+            //Must make a copy because arguments is an array-like object (not instanceof Array), causing suboptimal JSON encoding
+            var args = Array.prototype.slice.call(arguments, 0);
+            m.@com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavascriptManagerConnector::sendRpc(Ljava/lang/String;Lcom/google/gwt/core/client/JsArray;)(name, args);
+        });
+    }-*/;
+
+    // TODO only remove what we actually added
+    private native void removeCallback(String name)
+    /*-{
+        delete $wnd[name];
+    }-*/;
+
+    public void sendRpc(String name, JsArray<JavaScriptObject> arguments) {
+        Object[] parameters = new Object[] { name, new JSONArray(arguments) };
+
+        /*
+         * Must invoke manually as the RPC interface can't be used in GWT
+         * because of the JSONArray parameter
+         */
+        getConnection()
+                .addMethodInvocationToQueue(
+                        new MethodInvocation(
+                                getConnectorId(),
+                                "com.vaadin.ui.JavascriptManager$JavascriptCallbackRpc",
+                                "call", parameters), true);
+    }
+
+    @Override
+    public JavascriptManagerState getState() {
+        return (JavascriptManagerState) super.getState();
+    }
+}
diff --git a/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java
new file mode 100644 (file)
index 0000000..77794ff
--- /dev/null
@@ -0,0 +1,22 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.terminal.gwt.client.extensions.javascriptmanager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.vaadin.terminal.gwt.client.communication.SharedState;
+
+public class JavascriptManagerState extends SharedState {
+    private Set<String> names = new HashSet<String>();
+
+    public Set<String> getNames() {
+        return names;
+    }
+
+    public void setNames(Set<String> names) {
+        this.names = names;
+    }
+}
index fb2d289484c71aee62a9762efee685fd6037cfad..b693514a6657f1ce01c9fd76f32f926ab37ee65c 100644 (file)
@@ -123,6 +123,9 @@ public class JsonCodec implements Serializable {
         // Try to decode object using fields
         if (value == JSONObject.NULL) {
             return null;
+        } else if (targetType == JSONObject.class
+                || targetType == JSONArray.class) {
+            return value;
         } else {
             return decodeObject(targetType, (JSONObject) value, application);
         }
diff --git a/src/com/vaadin/ui/JavascriptCallback.java b/src/com/vaadin/ui/JavascriptCallback.java
new file mode 100644 (file)
index 0000000..4a34167
--- /dev/null
@@ -0,0 +1,13 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+
+import com.vaadin.external.json.JSONArray;
+
+public interface JavascriptCallback extends Serializable {
+    public void call(JSONArray arguments);
+}
diff --git a/src/com/vaadin/ui/JavascriptManager.java b/src/com/vaadin/ui/JavascriptManager.java
new file mode 100644 (file)
index 0000000..e52962f
--- /dev/null
@@ -0,0 +1,53 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.terminal.AbstractExtension;
+import com.vaadin.terminal.gwt.client.communication.ServerRpc;
+import com.vaadin.terminal.gwt.client.extensions.javascriptmanager.JavascriptManagerState;
+
+public class JavascriptManager extends AbstractExtension {
+    private Map<String, JavascriptCallback> callbacks = new HashMap<String, JavascriptCallback>();
+
+    // Can not be defined in client package as this JSONArray is not available
+    // in GWT
+    public interface JavascriptCallbackRpc extends ServerRpc {
+        public void call(String name, JSONArray arguments);
+    }
+
+    public JavascriptManager() {
+        registerRpc(new JavascriptCallbackRpc() {
+            public void call(String name, JSONArray arguments) {
+                JavascriptCallback callback = callbacks.get(name);
+                // TODO error handling
+                callback.call(arguments);
+            }
+        });
+    }
+
+    @Override
+    public JavascriptManagerState getState() {
+        return (JavascriptManagerState) super.getState();
+    }
+
+    public void addCallback(String name, JavascriptCallback javascriptCallback) {
+        callbacks.put(name, javascriptCallback);
+        if (getState().getNames().add(name)) {
+            requestRepaint();
+        }
+    }
+
+    public void removeCallback(String name) {
+        callbacks.remove(name);
+        if (getState().getNames().remove(name)) {
+            requestRepaint();
+        }
+    }
+
+}
index 8792bf1912b4de40c9e65f96011482426c763e02..9814084cbcf7b167f750d100c0b7e427aee33541 100644 (file)
@@ -408,6 +408,8 @@ public abstract class Root extends AbstractComponentContainer implements
     private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker(
             this);
 
+    private JavascriptManager javascriptManager;
+
     private RootServerRpc rpc = new RootServerRpc() {
         public void click(MouseEventDetails mouseDetails) {
             fireEvent(new ClickEvent(Root.this, mouseDetails));
@@ -1590,4 +1592,14 @@ public abstract class Root extends AbstractComponentContainer implements
         return dirtyConnectorTracker;
     }
 
+    public JavascriptManager getJavascriptManager() {
+        if (javascriptManager == null) {
+            // Create and attach on first use
+            javascriptManager = new JavascriptManager();
+            addExtension(javascriptManager);
+        }
+
+        return javascriptManager;
+    }
+
 }
diff --git a/tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.html b/tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.html
new file mode 100644 (file)
index 0000000..1ec959a
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://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.features.JavascriptManagerTest?restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfeaturesJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[4]</td>
+       <td>1. Got 4 arguments</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfeaturesJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[3]</td>
+       <td>2. Argument 1 as a number: 42</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfeaturesJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[2]</td>
+       <td>3. Argument 2 as a string: text</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfeaturesJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[1]</td>
+       <td>4. Argument 3.p as a boolean: true</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfeaturesJavascriptManagerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[0]</td>
+       <td>5. Argument 4 is JSONObject.NULL: true</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.java b/tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.java
new file mode 100644 (file)
index 0000000..18abf9f
--- /dev/null
@@ -0,0 +1,51 @@
+/* 
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.tests.features;
+
+import com.vaadin.external.json.JSONArray;
+import com.vaadin.external.json.JSONException;
+import com.vaadin.external.json.JSONObject;
+import com.vaadin.terminal.WrappedRequest;
+import com.vaadin.tests.components.AbstractTestRoot;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.JavascriptCallback;
+
+public class JavascriptManagerTest extends AbstractTestRoot {
+
+    private Log log = new Log(5);
+
+    @Override
+    protected void setup(WrappedRequest request) {
+        addComponent(log);
+        getJavascriptManager().addCallback("testing", new JavascriptCallback() {
+            public void call(JSONArray arguments) {
+                try {
+                    log.log("Got " + arguments.length() + " arguments");
+                    log.log("Argument 1 as a number: " + arguments.getInt(0));
+                    log.log("Argument 2 as a string: " + arguments.getString(1));
+                    log.log("Argument 3.p as a boolean: "
+                            + arguments.getJSONObject(2).getBoolean("p"));
+                    log.log("Argument 4 is JSONObject.NULL: "
+                            + (arguments.get(3) == JSONObject.NULL));
+                } catch (JSONException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        executeJavaScript("window.testing(42, 'text', {p: true}, null)");
+    }
+
+    @Override
+    protected String getTestDescription() {
+        return "Test javascript callback handling by adding a callback and invoking the javascript.";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}