aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeif Åstrand <leif@vaadin.com>2012-06-11 21:52:26 +0300
committerLeif Åstrand <leif@vaadin.com>2012-06-11 21:52:26 +0300
commit9b757f047fc8450d8a7d3d7aa5a7b7e43bfa1312 (patch)
treee3814cb6b78cb90570f2b9c31978728e44c7ec33
parent24e3409e6ff117ff954f36a9c9378690c76634f8 (diff)
downloadvaadin-framework-9b757f047fc8450d8a7d3d7aa5a7b7e43bfa1312.tar.gz
vaadin-framework-9b757f047fc8450d8a7d3d7aa5a7b7e43bfa1312.zip
Initial javascript callback support (#6730)
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java78
-rw-r--r--src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java22
-rw-r--r--src/com/vaadin/terminal/gwt/server/JsonCodec.java3
-rw-r--r--src/com/vaadin/ui/JavascriptCallback.java13
-rw-r--r--src/com/vaadin/ui/JavascriptManager.java53
-rw-r--r--src/com/vaadin/ui/Root.java12
-rw-r--r--tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.html47
-rw-r--r--tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.java51
8 files changed, 279 insertions, 0 deletions
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
index 0000000000..979a6d22c4
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java
@@ -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
index 0000000000..77794ffdca
--- /dev/null
+++ b/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java
@@ -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;
+ }
+}
diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
index fb2d289484..b693514a66 100644
--- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java
+++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java
@@ -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
index 0000000000..4a34167256
--- /dev/null
+++ b/src/com/vaadin/ui/JavascriptCallback.java
@@ -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
index 0000000000..e52962f177
--- /dev/null
+++ b/src/com/vaadin/ui/JavascriptManager.java
@@ -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();
+ }
+ }
+
+}
diff --git a/src/com/vaadin/ui/Root.java b/src/com/vaadin/ui/Root.java
index 8792bf1912..9814084cbc 100644
--- a/src/com/vaadin/ui/Root.java
+++ b/src/com/vaadin/ui/Root.java
@@ -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
index 0000000000..1ec959abce
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.html
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://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
index 0000000000..18abf9f8b8
--- /dev/null
+++ b/tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.java
@@ -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;
+ }
+
+}