blob: 0fbd783dc6a48d74f0aafb59067b75537a5b8805 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Red Hat and others. All rights reserved.
* The contents of this file are made available under the terms
* of the GNU Lesser General Public License (LGPL) Version 2.1 that
* accompanies this distribution (lgpl-v21.txt). The LGPL is also
* available at http://www.gnu.org/licenses/lgpl.html. If the version
* of the LGPL at http://www.gnu.org is different to the version of
* the LGPL accompanying this distribution and there is any conflict
* between the two license versions, the terms of the LGPL accompanying
* this distribution shall govern.
*
* Contributors:
* Red Hat - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.browser;
import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.gtk.*;
import org.eclipse.swt.widgets.*;
/**
* Logic for Webkit to interact with it's Webkit extension via GDBus.
*
* While this class supports quite a bit of GDBus and gvariant support, it is by no means a complete
* implementation and it's tailored to support Java to Javascript conversion. (E.g all Numbers are converted to Double).
* If this is ever to be used outside of Webkit, then care must be taken to deal with
* cases that are not currently implemented/used. See: WebKitGTK.java 'TYPE NOTES'
*
* For hygiene purposes, GVariant types should not be leaving this class. Convert on the way in/out.
*
* @category gdbus
*/
class WebkitGDBus {
/*
* If any of the GDBus names/paths/interfaces need to be changed, then they also need to be changed
* in the extension (webkitgtk_extension.c).
*/
/* WEBKITGDBUS_DBUS_NAME isn't actually used but is here for informative purposes */
@SuppressWarnings("unused")
private static final String WEBKITGDBUS_DBUS_NAME = "org.eclipse.swt";
private static final byte [] WEBKITGDBUS_OBJECT_PATH = Converter.javaStringToCString("/org/eclipse/swt/gdbus");
private static final String WEBKITGDBUS_INTERFACE_NAME_JAVA = "org.eclipse.swt.gdbusInterface";
private static final byte [] WEBKITGDBUS_INTERFACE_NAME = Converter.javaStringToCString("org.eclipse.swt.gdbusInterface");
/* Extension connection details, in byte [] form */
private static final byte [] EXTENSION_DBUS_NAME = Converter.javaStringToCString("org.eclipse.swt.webkitgtk_extension");
private static final byte [] EXTENSION_OBJECT_PATH = Converter.javaStringToCString("/org/eclipse/swt/webkitgtk_extension/gdbus");
private static final byte [] EXTENSION_INTERFACE_NAME = Converter.javaStringToCString("org.eclipse.swt.webkitgtk_extension.gdbusInterface");
/** Extension GDBusServer client address */
private static String EXTENSION_DBUS_SERVER_CLIENT_ADDRESS;
/* Accepted GDBus methods */
private static final String webkit2callJava = WebKit.WebKitExtension.getJavaScriptFunctionName();
private static final String webkitWebExtensionIdentifier = WebKit.WebKitExtension.getWebExtensionIdentifier();
/* Connections */
/** GDBusConnection from the web extension */
static long connectionFromExtension;
/** GDBusConnection to the web extension */
static long connectionToExtension;
/** A field that is set to true if the proxy connection has been established, false otherwise. */
static boolean connectionToExtensionCreated;
/* Server related objects */
/** GDBusServer for the main SWT process */
private static long gDBusServer = 0;
/** GDBusAuthObserver for the server */
private static long authObserver = 0;
/** GUID of the GDBusServer */
private static long guid = 0;
/** Display this GDBus class is "attached" to */
static Display display;
// BrowserFunction logic
/** Set to true if there are <code>BrowserFunction</code> objects waiting to be registered with the web extension.*/
static boolean functionsPending;
/**
* HashMap that stores any BrowserFunctions which have been created but not yet registered with the web extension.
* These functions will be registered with the web extension as soon as the proxy to the extension is set up.
*
* The format of the HashMap is (page ID, list of function string and URL).
*/
static HashMap<Long, ArrayList<ArrayList<String>>> pendingBrowserFunctions = new HashMap<>();
/**
* Interface is read/parsed at run time. No compilation with gdbus-code-gen necessary.
*
* Note,
* - When calling a method via g_dbus_proxy_call_sync(..g_variant params..),
* the g_variant that describes parameters should only mirror incoming parameters.
* Each type is a separate argument.
* e.g:
* g_variant xml:
* "(si)", "string", 42 .. arg type='s'
* .. arg type='i'
*
* - Nested parameters need to have a 2nd bracket around them.
* e.g:
* g_variant xml:
* "((r)i)", *gvariant, 42 .. arg type='r'
* .. arg type='i'
*
* - '@' is a pointer to a gvariant. so '@r' is a pointer to nested type, i.e *gvariant
*
* To understand the mappings, it's good to understand DBus and GVariant's syntax:
* https://dbus.freedesktop.org/doc/dbus-specification.html#idm423
* https://developer.gnome.org/glib/stable/glib-GVariantType.html
*
* Be mindful about only using supported DBUS_TYPE_* , as convert* methods might fail otherwise.
* Alternatively, modify convert* methods.
*/
private static final byte [] WEBKITGDBUS_INTROSPECTION_XML = Converter.javaStringToCString(
"<node>"
+ " <interface name='" + WEBKITGDBUS_INTERFACE_NAME_JAVA + "'>"
+ " <method name='" + webkit2callJava + "'>"
+ " <arg type='"+ OS.DBUS_TYPE_STRING + "' name='webViewPtr' direction='in'/>"
+ " <arg type='"+ OS.DBUS_TYPE_DOUBLE + "' name='index' direction='in'/>"
+ " <arg type='"+ OS.DBUS_TYPE_STRING + "' name='token' direction='in'/>"
+ " <arg type='" + OS.DBUS_TYPE_SINGLE_COMPLETE + "' name='arguments' direction='in'/>"
+ " <arg type='" + OS.DBUS_TYPE_SINGLE_COMPLETE + "' name='result' direction='out'/>"
+ " </method>"
+ " <method name='" + webkitWebExtensionIdentifier + "'>"
+ " <arg type='"+ OS.DBUS_TYPE_STRING + "' name='webExtensionServerAddress' direction='in'/>"
+ " <arg type='"+ OS.DBUS_TYPE_STRUCT_ARRAY_BROWSER_FUNCS + "' name='result' direction='out'/>"
+ " </method>"
+ " </interface>"
+ "</node>");
/**
* GDBus/DBus doesn't have a notion of Null.
* To get around this, we use magic numbers to represent special cases.
* Currently this is specific to Webkit to deal with Javascript data type conversions.
* @category gdbus */
private static final byte SWT_DBUS_MAGIC_NUMBER_EMPTY_ARRAY = 101;
/** @category gdbus */
private static final byte SWT_DBUS_MAGIC_NUMBER_NULL = 48;
/** Callback for GDBus method handling */
private static Callback handleMethodCB;
/** Callback for incoming connections to WebkitGDBus' server */
private static Callback newConnectionCB;
/** Callback for creating a new connection to the web extension */
private static Callback newConnectionToExtensionCB;
/** Callback for authenticating connections to WebkitGDBus' server */
private static Callback authenticatePeerCB;
/** Callback for asynchronous proxy calls to the extension */
private static Callback callExtensionAsyncCB;
static {
handleMethodCB = new Callback (WebkitGDBus.class, "handleMethodCB", 8); //$NON-NLS-1$
callExtensionAsyncCB = new Callback (WebkitGDBus.class, "callExtensionAsyncCB", 3); //$NON-NLS-1$
newConnectionCB = new Callback (WebkitGDBus.class, "newConnectionCB", 3); //$NON-NLS-1$
newConnectionToExtensionCB = new Callback (WebkitGDBus.class, "newConnectionToExtensionCB", 3); //$NON-NLS-1$
authenticatePeerCB = new Callback (WebkitGDBus.class, "authenticatePeerCB", 4); //$NON-NLS-1$
}
/** True iff the GDBusServer has been initialized */
static boolean initialized;
/** True iff this class has been attached to a Display */
static boolean attachedToDisplay;
/** This method is in an internal class and is not intended to be referenced by clients. */
static long init () {
if (initialized) {
return gDBusServer;
}
initialized = true;
// Generate address and GUID
long address = construct_server_address();
authObserver = OS.g_dbus_auth_observer_new();
guid = OS.g_dbus_generate_guid();
// Create server
long [] error = new long [1];
gDBusServer = OS.g_dbus_server_new_sync(address, OS.G_DBUS_SERVER_FLAGS_NONE, guid, authObserver, 0, error);
// Connect authentication and incoming connections signals to the newly created server, and start it
if (gDBusServer != 0) {
OS.g_signal_connect(gDBusServer, OS.new_connection, newConnectionCB.getAddress(), 0);
OS.g_signal_connect(authObserver, OS.authorize_authenticated_peer, authenticatePeerCB.getAddress(), 0);
OS.g_dbus_server_start(gDBusServer);
} else {
System.err.println("SWT WebKitGDBus: error creating DBus server " + Display.extractFreeGError(error[0]));
}
return gDBusServer;
}
/**
* Sets the Display for this class. This allows WebkitGDBus to attach its servers and related objects
* to the provided Display. When the Display is disposed, it will will release the GDBus related
* resources.
*
* @param displayToSet the Display to set
*/
static void setDisplay (Display displayToSet) {
if (attachedToDisplay) {
return;
} else {
display = displayToSet;
/*
* Add the GDBusServer, GDBusAuthObserver, and GUID to the Display.
* Note that we don't add the connections yet, because they likely
* don't exist. Those are added in callbacks as they come in.
*/
if (gDBusServer != 0) display.dBusServers.add(gDBusServer);
if (authObserver != 0) display.dBusAuthObservers.add(authObserver);
if (guid != 0) display.dBusGUIDS.add(guid);
attachedToDisplay = true;
}
}
/**
* Constructs an address at which the GDBus server for the SWT main process
* can be reached.
*
* @return a pointer to the address
*/
private static long construct_server_address () {
// On Linux, the address required is used for as an abstract path. If abstract path is not supported
// one must create & manage temporary directories. See Bug562443.
byte [] address = Converter.wcsToMbcs("unix:tmpdir=/tmp/SWT-GDBusServer", true);
long addressPtr = OS.g_malloc(address.length);
C.memmove(addressPtr, address, address.length);
return addressPtr;
}
/**
* This is called when a client call one of the GDBus methods.
*
* Developer note:
* This method can be reached directly from GDBus cmd utility:
* gdbus call --session --dest org.eclipse.swt<UNIQUE_ID> --object-path /org/eclipse/swt/gdbus --method org.eclipse.swt.gdbusInterface.HelloWorld
* where as you tab complete, you append the UNIQUE_ID.
*
* @param connection GDBusConnection
* @param sender const gchar
* @param object_path const gchar
* @param interface_name const gchar
* @param method_name const gchar
* @param gvar_parameters GVariant
* @param invocation GDBusMethodInvocation
* @param user_data gpointer
* @return
*/
@SuppressWarnings("unused") // callback not directly called by SWT
private static long handleMethodCB (
long connection, long sender,
long object_path, long interface_name,
long method_name, long gvar_parameters,
long invocation, long user_data) {
String java_method_name = Converter.cCharPtrToJavaString(method_name, false);
Object result = null;
if (java_method_name != null) {
if (java_method_name.equals(webkit2callJava)) {
try {
Object [] java_parameters = (Object []) convertGVariantToJava(gvar_parameters);
result = WebKit.WebKitExtension.webkit2callJavaCallback(java_parameters);
} catch (Exception e) {
// gdbus should always return to prevent extension from hanging.
result = (String) WebBrowser.CreateErrorString (e.getLocalizedMessage ());
System.err.println("SWT WebkitGDBus: Exception occured in Webkit2 callback logic.");
}
} else if (java_method_name.equals(webkitWebExtensionIdentifier)) {
Object [] serverAddress = (Object []) convertGVariantToJava(gvar_parameters);
if (serverAddress[0] != null && serverAddress[0] instanceof String) {
EXTENSION_DBUS_SERVER_CLIENT_ADDRESS = (String) serverAddress[0];
// Connect to the extension's server by creating a connection asynchronously
createConnectionToExtension();
/*
* Return any pending BrowserFunctions that were created before WebkitGDBus
* was initialized.
*/
invokeReturnValueExtensionIdentifier(pendingBrowserFunctions, invocation);
} else {
System.err.println("SWT WebkitGDBus: error in web extension identification process."
+ " BrowserFunction may not work.");
}
return 0;
}
} else {
result = (String) "SWT WebkitGDBus: GDBus called an unknown method?";
System.err.println("SWT WebkitGDBus: Received a call from an unknown method: " + java_method_name);
}
invokeReturnValue(result, invocation);
return 0;
}
@SuppressWarnings("unused") // callback not directly called by SWT
private static long callExtensionAsyncCB (long source_object, long result, long user_data) {
long [] error = new long [1];
long gVariantResult = OS.g_dbus_connection_call_finish (connectionToExtension, result, error);
if (error[0] != 0) {
String msg = Display.extractFreeGError(error[0]);
System.err.println("SWT WebkitGDBus: there was an error executing something asynchronously with the extension (Java callback).");
System.err.println("SWT WebkitGDBus: the error message provided is " + msg);
}
OS.g_variant_unref(gVariantResult);
return 0;
}
@SuppressWarnings("unused") // callback not directly called by SWT
private static long authenticatePeerCB (long observer, long stream, long credentials, long user_data) {
boolean authorized = false;
if (credentials != 0) {
long error [] = new long [1];
long ownCredentials = OS.g_credentials_new();
authorized = OS.g_credentials_is_same_user(credentials, ownCredentials, error);
if (error[0] != 0) {
String msg = Display.extractFreeGError(error[0]);
System.err.println("SWT WebkitGDBus: error authenticating client connection to server " + msg);
}
OS.g_object_unref(ownCredentials);
}
return authorized ? 1 : 0;
}
@SuppressWarnings("unused") // callback not directly called by SWT
private static long newConnectionCB (long server, long connection, long user_data) {
long gdBusNodeInfo;
long [] error = new long [1];
gdBusNodeInfo = OS.g_dbus_node_info_new_for_xml(WEBKITGDBUS_INTROSPECTION_XML, error);
if (gdBusNodeInfo == 0 || error[0] != 0) {
System.err.println("SWT WebkitGDBus: failed to get introspection data");
}
assert gdBusNodeInfo != 0 : "SWT WebKitGDBus: introspection data should not be 0";
long interface_info = OS.g_dbus_node_info_lookup_interface(gdBusNodeInfo, WEBKITGDBUS_INTERFACE_NAME);
long vtable [] = { handleMethodCB.getAddress(), 0, 0 };
OS.g_dbus_connection_register_object(
connection,
WEBKITGDBUS_OBJECT_PATH,
interface_info,
vtable,
0, // user_data
0, // user_data_free_func
error);
if (error[0] != 0) {
System.err.println("SWT WebKitGDBus: failed to register object: " + WEBKITGDBUS_OBJECT_PATH);
return 0;
}
// Ref the connection and add it to the Display's list of connections
connectionFromExtension = OS.g_object_ref(connection);
if (attachedToDisplay && display != null) {
if (!display.dBusConnections.contains(connection)) display.dBusConnections.add(connectionFromExtension);
}
return 1;
}
@SuppressWarnings("unused") // callback not directly called by SWT
private static long newConnectionToExtensionCB (long sourceObject, long result, long user_data) {
long [] error = new long [1];
connectionToExtension = OS.g_dbus_connection_new_for_address_finish(result, error);
if (error[0] != 0) {
System.err.println("SWT WebKitGDBus: error finishing connection: " + Display.extractFreeGError(error[0]));
return 0;
} else {
connectionToExtensionCreated = true;
}
// Add the connections to the Display's list of connections
if (attachedToDisplay && display != null) {
if (!display.dBusConnections.contains(connectionToExtension)) display.dBusConnections.add(connectionToExtension);
}
return 0;
}
/**
* Returns a GVariant to the DBus invocation of the extension identifier method. When the extension
* is initialized it sends a DBus message to the SWT webkit instance. As a return value, the SWT webkit
* instance sends any BrowserFunctions that have been registered. If no functions have been registered,
* an "empty" function with a page ID of -1 is sent.
*
* @param map the HashMap of BrowserFunctions waiting to be registered in the extension, or null
* if you'd like to explicitly send an empty function signature
* @param invocation the GDBus invocation to return the value on
*/
private static void invokeReturnValueExtensionIdentifier (HashMap<Long, ArrayList<ArrayList<String>>> map,
long invocation) {
long resultGVariant;
long builder;
long type = OS.g_variant_type_new(OS.G_VARIANT_TYPE_ARRAY_BROWSER_FUNCS);
builder = OS.g_variant_builder_new(type);
if (builder == 0) return;
Object [] tupleArray = new Object[3];
boolean sendEmptyFunction;
if (map == null) {
sendEmptyFunction = true;
} else {
sendEmptyFunction = map.isEmpty() && !functionsPending;
}
/*
* No functions to register, send a page ID of -1 and empty strings.
*/
if (sendEmptyFunction) {
tupleArray[0] = (long)-1;
tupleArray[1] = "";
tupleArray[2] = "";
long tupleGVariant = convertJavaToGVariant(tupleArray);
if (tupleGVariant != 0) {
OS.g_variant_builder_add_value(builder, tupleGVariant);
} else {
System.err.println("SWT WebKitGDBus: error creating empty BrowserFunction GVariant tuple, skipping.");
}
} else {
for (long id : map.keySet()) {
ArrayList<ArrayList<String>> list = map.get(id);
if (list != null) {
for (ArrayList<String> stringList : list) {
Object [] stringArray = stringList.toArray();
if (stringArray.length > 2) {
System.err.println("SWT WebKitGDBus: String array with BrowserFunction and URL should never have"
+ "more than 2 Strings");
}
tupleArray[0] = id;
System.arraycopy(stringArray, 0, tupleArray, 1, 2);
long tupleGVariant = convertJavaToGVariant(tupleArray);
if (tupleGVariant != 0) {
OS.g_variant_builder_add_value(builder, tupleGVariant);
} else {
System.err.println("SWT WebKitGDBus: error creating BrowserFunction GVariant tuple, skipping.");
}
}
}
}
}
resultGVariant = OS.g_variant_builder_end(builder);
String typeString = Converter.cCharPtrToJavaString(OS.g_variant_get_type_string(resultGVariant), false);
if (!OS.DBUS_TYPE_STRUCT_ARRAY_BROWSER_FUNCS.equals(typeString)) {
System.err.println("SWT WebKitGDBus: an error packaging the GVariant occurred: type mismatch.");
}
long [] variants = {resultGVariant};
long finalGVariant = OS.g_variant_new_tuple(variants, 1);
OS.g_dbus_method_invocation_return_value(invocation, finalGVariant);
OS.g_variant_builder_unref(builder);
OS.g_variant_type_free(type);
return;
}
private static void invokeReturnValue (Object result, long invocation) {
long resultGVariant = 0;
try {
resultGVariant = convertJavaToGVariant(new Object [] {result}); // Result has to be a tuple.
} catch (SWTException e) {
// gdbus should always return to prevent extension from hanging.
String errMsg = (String) WebBrowser.CreateErrorString (e.getLocalizedMessage ());
resultGVariant = convertJavaToGVariant(new Object [] {errMsg});
}
OS.g_dbus_method_invocation_return_value(invocation, resultGVariant);
return; // void return value.
}
/**
* Asynchronously initializes a GDBusConnection to the web extension. Connection process
* will be confirmed when the newConnectionToExtension callback is called.
*/
private static void createConnectionToExtension() {
byte [] address = Converter.javaStringToCString(EXTENSION_DBUS_SERVER_CLIENT_ADDRESS);
long [] error = new long [1];
// Create connection asynchronously to avoid deadlock issues
OS.g_dbus_connection_new_for_address (address, OS.G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
0, 0, newConnectionToExtensionCB.getAddress(), 0);
if (error[0] != 0) {
System.err.println("SWT WebkitGDBus: error creating connection to the extension "
+ Display.extractFreeGError(error[0]));
}
}
/**
* Calls the web extension synchronously. Returns true if the operation succeeded, and false
* otherwise (or if the operation times out).
*
* @param params a pointer to the GVariant containing the parameters
* @param methodName a String representing the DBus method name in the extension
* @return an Object representing the return value from DBus in boolean form
*/
static Object callExtensionSync (long params, String methodName) {
long [] error = new long [1]; // GError **
long gVariant = OS.g_dbus_connection_call_sync(connectionToExtension, EXTENSION_DBUS_NAME, EXTENSION_OBJECT_PATH,
EXTENSION_INTERFACE_NAME, Converter.javaStringToCString(methodName), params,
0, OS.G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, 0, error);
if (error[0] != 0) {
String msg = Display.extractFreeGError(error[0]);
/*
* Don't print console warnings for timeout errors, as we can handle these ourselves.
* Note, most timeout errors happen only when running test cases, not during "normal" use.
*/
if (msg != null && (!msg.contains("Timeout") && !msg.contains("timeout"))) {
System.err.println("SWT WebKitGDBus: there was an error executing something synchronously with the extension.");
System.err.println("SWT WebKitGDBus: the error message is: " + msg);
return (Object) false;
}
return (Object) "timeout";
}
Object resultObject = gVariant != 0 ? convertGVariantToJava(gVariant) : (Object) false;
// Sometimes we get back tuples from GDBus, which get converted into Object arrays. In this case
// we only care about the first value, since the extension never returns anything more than that.
if (resultObject instanceof Object[]) {
return ((Object []) resultObject)[0];
}
return resultObject;
}
/**
* Calls the web extension asynchronously. Note, this method returning true does not
* guarantee the operation's success, it only means no errors occurred.
*
* @param params a pointer to the GVariant containing the parameters
* @param methodName a String representing the DBus method name in the extension
* @return true if the extension was called without errors, false otherwise
*/
static boolean callExtensionAsync (long params, String methodName) {
long [] error = new long [1]; // GError **
OS.g_dbus_connection_call(connectionToExtension, EXTENSION_DBUS_NAME, EXTENSION_OBJECT_PATH,
EXTENSION_INTERFACE_NAME, Converter.javaStringToCString(methodName), params,
0, OS.G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, 0, callExtensionAsyncCB.getAddress(), 0);
if (error[0] != 0) {
String msg = Display.extractFreeGError(error[0]);
System.err.println("SWT WebKitGDBus: there was an error executing something asynchronously "
+ "with the extension.");
System.err.println("SWT WebKitGDBus: the error message is: " + msg);
return false;
}
return true;
}
/* TYPE NOTES
*
* GDBus doesn't support all the types that we need. I used encoded 'byte' to translate some types.
*
* - 'null' is not supported. I thought to potentially use 'maybe' types, but they imply a possible NULL of a certain type, but not null itself.
* so I use 'byte=48' (meaning '0' in ASCII) to denote null.
*
* - Empty arrays/structs are not supported by gdbus.
* "Container types ... Empty structures are not allowed; there must be at least one type code between the parentheses"
* src: https://dbus.freedesktop.org/doc/dbus-specification.html
* I used byte=101 (meaning 'e' in ASCII) to denote empty array.
*
* In Javascript all Number types seem to be 'double', (int/float/double/short -> Double). So we convert everything into double accordingly.
*
* DBus Type info: https://dbus.freedesktop.org/doc/dbus-specification.html#idm423
* GDBus Type info: https://developer.gnome.org/glib/stable/glib-GVariantType.html
*/
/**
* Converts the given GVariant to a Java object.
* (Only subset of types is currently supported).
*
* We assume that the given gvariant does not contain errors. (checked by webextension first).
*
* @param gVariant a pointer to the native GVariant
*/
static Object convertGVariantToJava(long gVariant){
if (OS.g_variant_is_of_type(gVariant, OS.G_VARIANT_TYPE_BOOLEAN)){
return OS.g_variant_get_boolean(gVariant);
}
// see: WebKitGTK.java 'TYPE NOTES'
if (OS.g_variant_is_of_type(gVariant, OS.G_VARIANT_TYPE_BYTE)) {
byte byteVal = OS.g_variant_get_byte(gVariant);
switch (byteVal) {
case WebkitGDBus.SWT_DBUS_MAGIC_NUMBER_NULL:
return null;
case WebkitGDBus.SWT_DBUS_MAGIC_NUMBER_EMPTY_ARRAY:
return new Object [0];
default:
System.err.println("SWT WebKitGDBus: received an unsupported byte type via GDBus: " + byteVal);
break;
}
}
if (OS.g_variant_is_of_type(gVariant, OS.G_VARIANT_TYPE_DOUBLE)){
return OS.g_variant_get_double(gVariant);
}
if (OS.g_variant_is_of_type(gVariant, OS.G_VARIANT_TYPE_UINT64)){
return OS.g_variant_get_uint64(gVariant);
}
if (OS.g_variant_is_of_type(gVariant, OS.G_VARIANT_TYPE_STRING)){
return Converter.cCharPtrToJavaString(OS.g_variant_get_string(gVariant, null), false);
}
if (OS.g_variant_is_of_type(gVariant, OS.G_VARIANT_TYPE_TUPLE)){
int length = (int)OS.g_variant_n_children (gVariant);
Object[] result = new Object[length];
for (int i = 0; i < length; i++) {
result[i] = convertGVariantToJava (OS.g_variant_get_child_value(gVariant, i));
}
return result;
}
String typeString = Converter.cCharPtrToJavaString(OS.g_variant_get_type_string(gVariant), false);
SWT.error (SWT.ERROR_INVALID_ARGUMENT, new Throwable("Unhandled variant type " + typeString ));
return null;
}
/**
* Converts the given Java Object to a GVariant * representation.
* (Only subset of types is currently supported).
*
* We assume that input Object may contain invalid types.
*
* @return pointer GVariant *
*/
static long convertJavaToGVariant(Object javaObject) throws SWTException {
if (javaObject == null) {
return OS.g_variant_new_byte(WebkitGDBus.SWT_DBUS_MAGIC_NUMBER_NULL); // see: WebKitGTK.java 'TYPE NOTES'
}
if (javaObject instanceof Long) {
return OS.g_variant_new_uint64((Long) javaObject);
}
if (javaObject instanceof String) {
return OS.g_variant_new_string (Converter.javaStringToCString((String) javaObject));
}
if (javaObject instanceof Boolean) {
return OS.g_variant_new_boolean((Boolean) javaObject);
}
// We treat Integer, Long, Double, Short as a 'double' because in Javascript these are all 'double'.
// Note, they all extend 'Number' java type, so they are an instance of it.
if (javaObject instanceof Number) { // see: WebKitGTK.java 'TYPE NOTES'
return OS.g_variant_new_double (((Number) javaObject).doubleValue());
}
if (javaObject instanceof Object[]) {
Object[] arrayValue = (Object[]) javaObject;
int length = arrayValue.length;
if (length == 0) {
return OS.g_variant_new_byte(WebkitGDBus.SWT_DBUS_MAGIC_NUMBER_EMPTY_ARRAY); // see: WebKitGTK.java 'TYPE NOTES'
}
long variants[] = new long [length];
for (int i = 0; i < length; i++) {
variants[i] = convertJavaToGVariant(arrayValue[i]);
}
return OS.g_variant_new_tuple(variants, length);
}
System.err.println("SWT WebKitGDBus: invalid object being returned to JavaScript: " + javaObject.toString() + "\n"
+ "Only the following are supported: null, String, Boolean, Number(Long,Integer,Double...), Object[] of basic types");
throw new SWTException(SWT.ERROR_INVALID_ARGUMENT, "Given object is not valid: " + javaObject.toString());
}
}