Browse Source

Initial javascript callback support (#6730)

tags/7.0.0.alpha3
Leif Åstrand 12 years ago
parent
commit
9b757f047f

+ 78
- 0
src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerConnector.java View File

@@ -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();
}
}

+ 22
- 0
src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavascriptManagerState.java View File

@@ -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;
}
}

+ 3
- 0
src/com/vaadin/terminal/gwt/server/JsonCodec.java View 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);
}

+ 13
- 0
src/com/vaadin/ui/JavascriptCallback.java View File

@@ -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);
}

+ 53
- 0
src/com/vaadin/ui/JavascriptManager.java View File

@@ -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();
}
}

}

+ 12
- 0
src/com/vaadin/ui/Root.java View 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;
}

}

+ 47
- 0
tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.html View File

@@ -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>

+ 51
- 0
tests/testbench/com/vaadin/tests/features/JavascriptManagerTest.java View File

@@ -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;
}

}

Loading…
Cancel
Save