Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk')
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c20
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.c241
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.h59
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java97
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java261
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java11
8 files changed, 632 insertions, 59 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c
index de6febe682..686437b6e5 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c
@@ -2921,6 +2921,26 @@ JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1web_1view_1get_1main_1reso
}
#endif
+#ifndef NO__1webkit_1web_1view_1get_1page_1id
+JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1web_1view_1get_1page_1id)
+ (JNIEnv *env, jclass that, jintLong arg0)
+{
+ jintLong rc = 0;
+ WebKitGTK_NATIVE_ENTER(env, that, _1webkit_1web_1view_1get_1page_1id_FUNC);
+/*
+ rc = (jintLong)webkit_web_view_get_page_id(arg0);
+*/
+ {
+ WebKitGTK_LOAD_FUNCTION(fp, webkit_web_view_get_page_id)
+ if (fp) {
+ rc = (jintLong)((jintLong (CALLING_CONVENTION*)(jintLong))fp)(arg0);
+ }
+ }
+ WebKitGTK_NATIVE_EXIT(env, that, _1webkit_1web_1view_1get_1page_1id_FUNC);
+ return rc;
+}
+#endif
+
#ifndef NO__1webkit_1web_1view_1get_1progress
JNIEXPORT jdouble JNICALL WebKitGTK_NATIVE(_1webkit_1web_1view_1get_1progress)
(JNIEnv *env, jclass that, jintLong arg0)
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.c b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.c
index 2dbc28c16c..fd958309a8 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.c
@@ -13,7 +13,6 @@ gint32 parentUniqueId = 0;
guchar SWT_DBUS_MAGIC_NUMBER_EMPTY_ARRAY = 101;
guchar SWT_DBUS_MAGIC_NUMBER_NULL = 48;
-
// +-------------+----------------------------------------------------------------
// | Misc Helpers|
// +-------------+
@@ -35,6 +34,13 @@ static const gchar base_service_name[] = "org.eclipse.swt"; // Base name. Full n
static const gchar object_name[] = "/org/eclipse/swt/gdbus";
static const gchar interface[] = "org.eclipse.swt.gdbusInterface";
+typedef struct {
+ guint64 page_id;
+ const gchar *function;
+ const gchar *url;
+} BrowserFunction;
+
+GSList *function_list = NULL;
GDBusProxy *proxy = NULL; // The proxy that we work with
void proxy_init () {
@@ -72,10 +78,12 @@ GVariant * callMainProc(char * methodName, GVariant * params) {
// Error checking.
if (result == NULL) {
- if (error != NULL)
- g_error("SWT Webextension: Call failed because '%s.' Probably didn't handle type properly, could be an SWT bug. Signature: %s\n", error->message, g_variant_get_type_string(params));
- else
- g_error("SWT Webextension: Call failed for an unknown reason.\n");
+ if (error != NULL) {
+ g_error("SWT web extension: Call failed because '%s.'\n", error->message);
+ }
+ else {
+ g_error("SWT web extension: Call failed for an unknown reason.\n");
+ }
return NULL;
}
@@ -289,17 +297,124 @@ static JSValueRef webkit2callJava (JSContextRef context,
return retVal;
}
+static void web_page_created_callback(WebKitWebExtension *extension, WebKitWebPage *web_page, gpointer user_data) {
+ // Observation. This seems to be called only once.
+}
+
+/**
+ * Returns the main frame of the WebPage with the given ID
+ */
+static WebKitFrame *webkitgtk_extension_get_main_frame (const guint64 id) {
+ WebKitWebPage *web_page = webkit_web_extension_get_page (this_extension, id);
+ return webkit_web_page_get_main_frame (web_page);
+}
+
+/*
+ * Execute the Javascript for the given page and URL.
+ */
+static gboolean webkitgtk_extension_execute_script (const guint64 page_id, const gchar* script, const gchar* url) {
+ WebKitFrame *main_frame = webkitgtk_extension_get_main_frame (page_id);
+
+ JSStringRef url_string = JSStringCreateWithUTF8CString (url);
+ JSStringRef script_string = JSStringCreateWithUTF8CString (script);
+
+ JSGlobalContextRef context = webkit_frame_get_javascript_global_context (main_frame);
+
+ JSValueRef exception;
+ JSValueRef result = JSEvaluateScript(context, script_string, NULL, url_string, 0, &exception);
+ if (!result) {
+ JSStringRef exceptionIString = JSValueToStringCopy(context, exception, NULL);
+ size_t exceptionUTF8Size = JSStringGetMaximumUTF8CStringSize(exceptionIString);
+ char* exceptionUTF8 = (char*)malloc(exceptionUTF8Size);
+ JSStringGetUTF8CString(exceptionIString, exceptionUTF8, exceptionUTF8Size);
+ g_error("SWT web extension: failed to execute script exception: %s\n", exceptionUTF8);
+ free(exceptionUTF8);
+ JSStringRelease(exceptionIString);
+ }
+
+ JSStringRelease (url_string);
+ JSStringRelease (script_string);
+
+ return result != NULL;
+}
+
+void execute_browser_functions(gconstpointer item, gpointer page) {
+ BrowserFunction *function = (BrowserFunction *) item;
+ if (function != NULL && function->page_id == GPOINTER_TO_UINT(page)) {
+ webkitgtk_extension_execute_script(function->page_id, function->function, function->url);
+ }
+ return;
+}
+
+gint find_browser_function (gconstpointer item, gconstpointer target) {
+ BrowserFunction *element = (BrowserFunction *) item;
+ BrowserFunction *remove = (BrowserFunction *) target;
+ if (element->page_id == remove->page_id && g_strcmp0(element->function, remove->function) == 0 &&
+ g_strcmp0(element->url, remove->url) == 0) {
+ return 0;
+ }
+ return 1;
+}
+
+void add_browser_function(guint64 page_id, const gchar *function, const gchar *url) {
+ BrowserFunction *func = g_slice_new0(BrowserFunction);
+ func->page_id = page_id;
+ func->function = function;
+ func->url = url;
+ function_list = g_slist_append(function_list, func);
+}
+
+void remove_browser_function(guint64 page_id, const gchar *function, const gchar *url) {
+ BrowserFunction *func = g_slice_new0(BrowserFunction);
+ func->page_id = page_id;
+ func->function = function;
+ func->url = url;
+ GSList *to_remove = g_slist_find_custom(function_list, func, find_browser_function);
+ if (to_remove != NULL) {
+ function_list = g_slist_delete_link(function_list, to_remove);
+ }
+ g_slice_free(BrowserFunction, func);
+}
+
+void unpack_browser_function_array(GVariant *array) {
+ GVariantIter iter;
+ GVariant *child;
+
+ g_variant_iter_init (&iter, array);
+ while ((child = g_variant_iter_next_value (&iter))) {
+ gsize length = (int)g_variant_n_children (child);
+ if (length > 3) {
+ // If the length is longer than three, something went wrong and this tuple should be skipped
+ g_warning("SWT web extension: there was an error unpacking the GVariant tuple for a BrowserFunction in the web extension.\n");
+ continue;
+ }
+ guint64 page = g_variant_get_uint64(g_variant_get_child_value(child, 0));
+ if (page == -1) {
+ // Empty or malformed BrowserFunction, skip this one
+ continue;
+ } else {
+ const gchar *function = g_variant_get_string(g_variant_get_child_value(child, 1), NULL);
+ const gchar *url = g_variant_get_string(g_variant_get_child_value(child, 2), NULL);
+ if (function != NULL && url != NULL) {
+ add_browser_function(page, function, url);
+ } else {
+ g_warning("SWT web extension: there was an error unpacking the function string or URL.\n");
+ }
+ }
+ g_variant_unref (child);
+ }
+}
/*
- * Everytime a webpage is loaded, we should re-register the 'webkit2callJava' function.
+ * Every time a webpage is loaded, we should re-register the 'webkit2callJava' function.
+ * Additionally, we re-register all BrowserFunctions that are stored in the function_list
+ * GSList.
*/
-static void window_object_cleared_callback (WebKitScriptWorld *world,
- WebKitWebPage *web_page,
+static void window_object_cleared_callback (WebKitScriptWorld *world, WebKitWebPage *web_page,
WebKitFrame *frame,
- gpointer user_data)
-{
- // Observation: This is called everytime a webpage is loaded.
- JSGlobalContextRef jsContext;
+ gpointer user_data) {
+ // Observation: This is called every time a webpage is loaded.
+ JSGlobalContextRef jsContext;
JSObjectRef globalObject;
JSValueRef exception = 0;
@@ -314,23 +429,113 @@ static void window_object_cleared_callback (WebKitScriptWorld *world,
if (exception) {
g_print("OJSObjectSetProperty exception occurred");
}
+
+ /*
+ * Iterate over the list of BrowserFunctions and execute each one of them for the current page.
+ * This ensures that BrowserFunctions are not lost on page reloads. See bug 536141.
+ */
+ if (function_list != NULL) {
+ guint64 page_id = webkit_web_page_get_id (web_page);
+ if (page_id != -1) {
+ g_slist_foreach(function_list, (GFunc)execute_browser_functions, GUINT_TO_POINTER(page_id));
+ } else {
+ g_warning("SWT web extension: there was an error fetching the page ID in the object_cleared callback.\n");
+ }
+ }
}
-static void web_page_created_callback(WebKitWebExtension *extension, WebKitWebPage *web_page, gpointer user_data) {
- // Observation. This seems to be called only once.
+static void
+webkitgtk_extension_handle_method_call (GDBusConnection *connection, const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data) {
+ gboolean result = FALSE;
+ const gchar *script;
+ const gchar *url;
+ guint64 page_id;
+ // Check method names
+ if (g_strcmp0(method_name, "webkitgtk_extension_register_function") == 0) {
+ g_variant_get(parameters, "(t&s&s)", &page_id, &script, &url);
+ if (page_id != -1) {
+ result = TRUE;
+ // Return before processing the linked list, to prevent DBus from hanging
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", result));
+ add_browser_function(page_id, script, url);
+ return;
+ }
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", result));
+ return;
+ }
+ if (g_strcmp0(method_name, "webkitgtk_extension_deregister_function") == 0) {
+ g_variant_get(parameters, "(t&s&s)", &page_id, &script, &url);
+ if (page_id != -1) {
+ result = TRUE;
+ // Return before processing the linked list, to prevent DBus from hanging
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", result));
+ remove_browser_function(page_id, script, url);
+ return;
+ }
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", result));
+ return;
+ }
+ g_error ("UNKNOWN method %s\n", method_name);
}
-G_MODULE_EXPORT void
-webkit_web_extension_initialize_with_user_data(WebKitWebExtension *extension, GVariant *user_data)
-{
+static const GDBusInterfaceVTable interface_vtable = {webkitgtk_extension_handle_method_call, NULL, NULL};
+
+static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) {
+ dbus_interface = g_dbus_node_info_lookup_interface(dbus_node, WEBKITGTK_EXTENSION_DBUS_INTERFACE);
+ guint registration_id = g_dbus_connection_register_object(connection,
+ webkitgtk_extension_dbus_path,
+ dbus_interface,
+ &interface_vtable, NULL, /* user_data */
+ NULL, /* user_data_free_func */
+ NULL); /* GError** */
+ g_assert(registration_id > 0);
+
+ GVariant *g_var_result = callMainProc("webkitWebExtensionIdentifer", g_variant_new ("(ss)",
+ webkitgtk_extension_dbus_name, webkitgtk_extension_dbus_path));
+ if (g_variant_is_of_type(g_var_result, G_VARIANT_TYPE_TUPLE)) {
+ unpack_browser_function_array(g_variant_get_child_value(g_var_result, 0));
+ } else {
+ g_warning("SWT web extension: on_bus_acquired return value from SWT was an unexpected type (not a tuple).\n");
+ }
+ return;
+}
+
+G_MODULE_EXPORT void webkit_web_extension_initialize_with_user_data(WebKitWebExtension *extension, GVariant *user_data) {
// To debug this extension:
// - ensure this is build with debug flags (look for '-g*' in make_linux, or 'SWT_LIB_DEBUG' macro.
// - connect to WebKitWebProcess with pid of this extension. Use below to print it:
- // g_print("Webext pid: %d (To debug, attach to WebKitWebProcess with this pid)\n", getpid());
+ // g_print("Webext pid: %d (To debug, attach to WebKitWebProcess with this pid)\n", getpid());
+ this_extension = extension;
parentUniqueId = g_variant_get_int32(user_data);
g_signal_connect(extension, "page-created", G_CALLBACK(web_page_created_callback), NULL);
// To hook into javascript execution:
g_signal_connect (webkit_script_world_get_default (), "window-object-cleared", G_CALLBACK (window_object_cleared_callback), NULL);
+
+ // Create DBus server for this web extension
+ webkitgtk_extension_dbus_name = combineStrInt((char *) WEBKITGTK_EXTENSION_DBUS_NAME_PREFIX, (gint32) getpid());
+ webkitgtk_extension_dbus_path = combineStrInt((char *) WEBKITGTK_EXTENSION_DBUS_PATH_PREFIX, (gint32) getpid());
+
+ dbus_introspection_xml = g_new (gchar, strlen(dbus_introspection_xml_template) + strlen(WEBKITGTK_EXTENSION_DBUS_INTERFACE) + 1);
+ g_sprintf (dbus_introspection_xml, dbus_introspection_xml_template, WEBKITGTK_EXTENSION_DBUS_INTERFACE);
+ dbus_node = g_dbus_node_info_new_for_xml (dbus_introspection_xml, NULL);
+ g_assert (dbus_node != NULL);
+
+ guint owner_id;
+ owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ webkitgtk_extension_dbus_name,
+ G_BUS_NAME_OWNER_FLAGS_REPLACE | G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
+ on_bus_acquired,
+ NULL, /* on_name_acquired */
+ NULL, /* on_name_lost */
+ NULL,
+ NULL);
+ g_assert (owner_id != 0);
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.h b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.h
index 72a507ba81..4d2713cd76 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.h
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_extension.h
@@ -1,4 +1,4 @@
-/*******************************************************************************
+ /*******************************************************************************
* 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
@@ -12,12 +12,9 @@
* Contributors:
* Red Hat - initial API and implementation
*******************************************************************************/
-
-
#ifndef INC_webkit_extension_H
#define INC_webkit_extension_H
-
#include <string.h>
#include <glib.h>
@@ -29,12 +26,10 @@
#include <unistd.h>
#include <stdio.h>
-
// These 2 are only for getpid();
#include <sys/types.h>
#include <unistd.h>
-
#include <webkit2/webkit-web-extension.h>
#include <JavaScriptCore/JavaScript.h>
@@ -42,22 +37,40 @@
#include <JavaScriptCore/JSObjectRef.h>
#include <JavaScriptCore/JSStringRef.h>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+#define WEBKITGTK_EXTENSION_DBUS_NAME_PREFIX "org.eclipse.swt.webkitgtk_extension"
+#define WEBKITGTK_EXTENSION_DBUS_PATH_PREFIX "/org/eclipse/swt/webkitgtk_extension/gdbus/"
+#define WEBKITGTK_EXTENSION_DBUS_INTERFACE "org.eclipse.swt.webkitgtk_extension.gdbusInterface"
+
+#define WEBKIT_MAIN_PROCESS_DBUS_NAME_PREFIX "org.eclipse.swt"
+#define WEBKIT_MAIN_PROCESS_DBUS_PATH_PREFIX "/org/eclipse/swt/gdbus/"
+
+static gchar* webkitgtk_extension_dbus_name;
+static gchar* webkitgtk_extension_dbus_path;
+
+static WebKitWebExtension *this_extension;
+
+static GDBusNodeInfo *dbus_node;
+static GDBusInterfaceInfo *dbus_interface;
+static gchar* dbus_introspection_xml;
+static gchar* dbus_introspection_xml_template =
+"<node>"
+ "<interface name='%s'>"
+
+ "<method name='webkitgtk_extension_register_function'>"
+ "<arg type='t' name='page_id' direction='in'/>"
+ "<arg type='s' name='script' direction='in'/>"
+ "<arg type='s' name='url' direction='in'/>"
+ "<arg type='b' name='result' direction='out'/>"
+ "</method>"
+
+ "<method name='webkitgtk_extension_deregister_function'>"
+ "<arg type='t' name='page_id' direction='in'/>"
+ "<arg type='s' name='script' direction='in'/>"
+ "<arg type='s' name='url' direction='in'/>"
+ "<arg type='b' name='result' direction='out'/>"
+ "</method>"
+
+ "</interface>"
+"</node>";
#endif /*INC_webkit_extension_H*/
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c
index 32ad50e13e..84ee7e587d 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c
@@ -167,6 +167,7 @@ char * WebKitGTK_nativeFunctionNames[] = {
"_1webkit_1web_1view_1get_1load_1status",
"_1webkit_1web_1view_1get_1main_1frame",
"_1webkit_1web_1view_1get_1main_1resource",
+ "_1webkit_1web_1view_1get_1page_1id",
"_1webkit_1web_1view_1get_1progress",
"_1webkit_1web_1view_1get_1settings",
"_1webkit_1web_1view_1get_1title",
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h
index dab44c08fe..22196ffafa 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h
@@ -177,6 +177,7 @@ typedef enum {
_1webkit_1web_1view_1get_1load_1status_FUNC,
_1webkit_1web_1view_1get_1main_1frame_FUNC,
_1webkit_1web_1view_1get_1main_1resource_FUNC,
+ _1webkit_1web_1view_1get_1page_1id_FUNC,
_1webkit_1web_1view_1get_1progress_FUNC,
_1webkit_1web_1view_1get_1settings_FUNC,
_1webkit_1web_1view_1get_1title_FUNC,
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java
index 81b218af3c..470ae9959f 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java
@@ -127,6 +127,7 @@ class WebKit extends WebBrowser {
*/
long /*int*/ webView;
long /*int*/ scrolledWindow;
+ long pageId;
/** Webkit1 only. Used by the externalObject for javascript callback to java. */
long /*int*/ webViewData;
@@ -419,12 +420,52 @@ class WebKit extends WebBrowser {
@Override
public void createFunction(BrowserFunction function) {
if (WEBKIT2) {
- if (!Webkit2Extension.gdbus_init()) {
- System.err.println("SWT Webkit Warning: Webkit extension failed to initialize. BrowserFunction will not work.: " + function.name);
+ if (!WebkitGDBus.initialized) {
+ System.err.println("SWT webkit: WebkitGDBus and/or Webkit2Extension not loaded, BrowserFunction will not work." +
+ "Tried to create "+ function.name);
return;
}
}
super.createFunction(function);
+ if (WEBKIT2) {
+ String url = this.getUrl().isEmpty() ? "nullURL" : this.getUrl();
+ /*
+ * If the proxy to the extension has not yet been loaded, store the BrowserFunction page ID,
+ * function string, and URL in a HashMap. Once the proxy to the extension is loaded, these
+ * functions will be sent to and registered in the extension.
+ */
+ if (!WebkitGDBus.proxyToExtension) {
+ WebkitGDBus.functionsPending = true;
+ ArrayList<ArrayList<String>> list = new ArrayList<>();
+ ArrayList<String> functionAndUrl = new ArrayList<>();
+ functionAndUrl.add(0, function.functionString);
+ functionAndUrl.add(1, url);
+ list.add(functionAndUrl);
+ ArrayList<ArrayList<String>> existing = WebkitGDBus.pendingBrowserFunctions.putIfAbsent(this.pageId, list);
+ if (existing != null) {
+ existing.add(functionAndUrl);
+ }
+ } else {
+ // If the proxy to the extension is already loaded, register the function in the extension via DBus
+ boolean successful = webkit_extension_modify_function(this.pageId, function.functionString, url, "register");
+ if (!successful) {
+ System.err.println("SWT webkit: failure registering BrowserFunction " + function.name);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void destroyFunction (BrowserFunction function) {
+ // Only deregister functions if the proxy to the extension has been loaded
+ if (WebkitGDBus.proxyToExtension && WEBKIT2) {
+ String url = this.getUrl().isEmpty() ? "nullURL" : this.getUrl();
+ boolean successful = webkit_extension_modify_function(this.pageId, function.functionString, url, "deregister");
+ if (!successful) {
+ System.err.println("SWT webkit: failure deregistering BrowserFunction from extension " + function.name);
+ }
+ }
+ super.destroyFunction(function);
}
private static String getInternalErrorMsg () {
@@ -458,6 +499,7 @@ class WebKit extends WebBrowser {
static class Webkit2Extension {
/** Note, if updating this, you need to change it also in webkitgtk_extension.c */
private static final String javaScriptFunctionName = "webkit2callJava"; // $NON-NLS-1$
+ private static final String webkitWebExtensionIdentifier = "webkitWebExtensionIdentifer"; // $NON-NLS-1$
private static Callback initializeWebExtensions_callback;
private static int uniqueID = OS.getpid();
@@ -470,6 +512,9 @@ class WebKit extends WebBrowser {
static String getJavaScriptFunctionName() {
return javaScriptFunctionName;
}
+ static String getWebExtensionIdentifer() {
+ return webkitWebExtensionIdentifier;
+ }
static String getJavaScriptFunctionDeclaration(long /*int*/ webView) {
return "if (!window.callJava) {\n"
+ " window.callJava = function callJava(index, token, args) {\n"
@@ -479,6 +524,12 @@ class WebKit extends WebBrowser {
}
static void init() {
+ /*
+ * Initialize GDBus before the extension, as the extension initialization callback at the C level
+ * sends data back to SWT via GDBus. Failure to load GDBus here will result in crashes.
+ * See bug 536141.
+ */
+ gdbus_init();
initializeWebExtensions_callback = new Callback(Webkit2Extension.class, "initializeWebExtensions_callback", void.class, new Type [] {long.class, long.class});
if (initializeWebExtensions_callback.getAddress() == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
if (WebKitGTK.webkit_get_minor_version() >= 4) { // Callback exists only since 2.04
@@ -1199,6 +1250,7 @@ public void create (Composite parent, int style) {
OS.g_signal_connect (webView, OS.focus_out_event, JSDOMEventProc.getAddress (), WIDGET_EVENT);
// if connecting any other special gtk event to webkit, add SWT.* to w2_passThroughSwtEvents above.
}
+ this.pageId = WebKitGTK.webkit_web_view_get_page_id (webView);
if (WEBKIT1) {
OS.g_signal_connect (webView, OS.button_press_event, JSDOMEventProc.getAddress (), 0);
OS.g_signal_connect (webView, OS.button_release_event, JSDOMEventProc.getAddress (), 0);
@@ -1522,6 +1574,46 @@ void nonBlockingExecute(String script) {
}
}
+/**
+ * Modifies a BrowserFunction in the web extension. This method can be used to register/deregister BrowserFunctions
+ * in the web extension, so that those BrowserFunctions are executed upon triggering of the object_cleared callback (in
+ * the extension, not in Java).
+ *
+ * This function will return true if: the operation succeeds synchronously, or if the synchronous call timed out and an
+ * asynchronous call was performed instead. All other cases will return false.
+ *
+ * Supported actions: "register" and "deregister"
+ *
+ * @param pageId the page ID of the WebKit instance/web page
+ * @param function the function string
+ * @param url the URL
+ * @param action the action being performed on the function, which will be used to form the DBus method name.
+ * @return true if the action succeeded (or was performed asynchronously), false if it failed
+ */
+private boolean webkit_extension_modify_function (long pageId, String function, String url, String action){
+ long /*int*/ args[] = { OS.g_variant_new_uint64(pageId),
+ OS.g_variant_new_string (Converter.javaStringToCString(function)),
+ OS.g_variant_new_string (Converter.javaStringToCString(url))};
+ final long /*int*/ argsTuple = OS.g_variant_new_tuple(args, args.length);
+ if (argsTuple == 0) return false;
+ String dbusMethodName = "webkitgtk_extension_" + action + "_function";
+ Object returnVal = WebkitGDBus.callExtensionSync(argsTuple, dbusMethodName);
+ if (returnVal instanceof Boolean) {
+ return (Boolean) returnVal;
+ } else if (returnVal instanceof String) {
+ String returnString = (String) returnVal;
+ /*
+ * Call the extension asynchronously if a synchronous call times out.
+ * Note: this is a pretty rare case, and usually only happens when running test cases.
+ * See bug 536141.
+ */
+ if ("timeout".equals(returnString)) {
+ return WebkitGDBus.callExtensionAsync(argsTuple, dbusMethodName);
+ }
+ }
+ return false;
+}
+
@Override
public boolean execute (String script) {
if (WEBKIT2){
@@ -3284,7 +3376,6 @@ long /*int*/ webkit_load_changed (long /*int*/ web_view, int status, long user_d
return handleLoadCommitted (uri, true);
}
case WebKitGTK.WEBKIT2_LOAD_FINISHED: {
- registerBrowserFunctions(); // Bug 508217
addEventHandlers (web_view, true);
long /*int*/ title = WebKitGTK.webkit_web_view_get_title (webView);
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java
index de2dc641ea..d7ec16e391 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebkitGDBus.java
@@ -15,6 +15,8 @@
package org.eclipse.swt.browser;
+import java.util.*;
+
import org.eclipse.swt.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.gtk.*;
@@ -33,11 +35,29 @@ import org.eclipse.swt.internal.gtk.*;
*/
class WebkitGDBus {
private static String DBUS_SERVICE_NAME;
- private static final String DBUS_OBJECT_NAME = "/org/eclipse/swt/gdbus";
+ private static final String DBUS_OBJECT_PATH = "/org/eclipse/swt/gdbus";
private static final String INTERFACE_NAME = "org.eclipse.swt.gdbusInterface";
+ private static final String EXTENSION_INTERFACE_NAME = "org.eclipse.swt.webkitgtk_extension.gdbusInterface";
+ private static String EXTENSION_DBUS_NAME;
+ private static String EXTENSION_DBUS_PATH;
/** Accepted methods over gdbus */
private static final String webkit2callJava = WebKit.Webkit2Extension.getJavaScriptFunctionName();
+ private static final String webkitWebExtensionIdentifier = WebKit.Webkit2Extension.getWebExtensionIdentifer();
+
+ /** Proxy connection to the web extension.*/
+ static long /*int*/ proxy;
+ /** A field that is set to true if the proxy connection has been established, false otherwise */
+ static boolean proxyToExtension;
+ /** 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<>();
/**
@@ -77,6 +97,11 @@ class WebkitGDBus {
+ " <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='webExtensionDbusName' direction='in'/>"
+ + " <arg type='"+ OS.DBUS_TYPE_STRING + "' name='webExtensionDbusPath' direction='in'/>"
+ + " <arg type='"+ OS.DBUS_TYPE_STRUCT_ARRAY_BROWSER_FUNCS + "' name='result' direction='out'/>"
+ + " </method>"
+ " </interface>"
+ "</node>";
@@ -96,6 +121,9 @@ class WebkitGDBus {
private static Callback onNameLostCallback;
private static Callback handleMethodCallback;
+ /** Callback for asynchronous proxy calls to the extension */
+ private static Callback callExtensionAsyncCallback;
+
static {
onBusAcquiredCallback = new Callback (WebkitGDBus.class, "onBusAcquiredCallback", 3); //$NON-NLS-1$
if (onBusAcquiredCallback.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
@@ -108,9 +136,12 @@ class WebkitGDBus {
handleMethodCallback = new Callback (WebkitGDBus.class, "handleMethodCallback", 8); //$NON-NLS-1$
if (handleMethodCallback.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
+
+ callExtensionAsyncCallback = new Callback (WebkitGDBus.class, "callExtensionAsyncCallback", 3); //$NON-NLS-1$
+ if (callExtensionAsyncCallback.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
}
- static private boolean initialized;
+ static boolean initialized;
/** This method is not intended to be referenced by clients. Internal class. */
static void init(String uniqueId) {
@@ -169,7 +200,7 @@ class WebkitGDBus {
// Other times it validates it fine. We ignore for now as 32bit will be dropped anyway.
OS.g_dbus_connection_register_object(
connection,
- Converter.javaStringToCString(DBUS_OBJECT_NAME),
+ Converter.javaStringToCString(DBUS_OBJECT_PATH),
interface_info,
vtable,
0, // user_data
@@ -177,7 +208,7 @@ class WebkitGDBus {
error);
if (error[0] != 0) {
- System.err.println("SWT WebkitGDBus: Failed to register object: " + DBUS_OBJECT_NAME);
+ System.err.println("SWT WebkitGDBus: Failed to register object: " + DBUS_OBJECT_PATH);
return 0;
}
}
@@ -236,19 +267,123 @@ class WebkitGDBus {
String java_method_name = Converter.cCharPtrToJavaString(method_name, false);
Object result = null;
- if (java_method_name != null && java_method_name.equals(webkit2callJava)) {
- try {
- Object [] java_parameters = (Object []) convertGVariantToJava(gvar_parameters);
- result = WebKit.Webkit2Extension.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 Webkit: Exception occured in Webkit2 callback logic. Bug?");
+ if (java_method_name != null) {
+ if (java_method_name.equals(webkit2callJava)) {
+ try {
+ Object [] java_parameters = (Object []) convertGVariantToJava(gvar_parameters);
+ result = WebKit.Webkit2Extension.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 Webkit: Exception occured in Webkit2 callback logic. Bug?");
+ }
+ } else if (java_method_name.equals(webkitWebExtensionIdentifier)) {
+ Object [] nameArray = (Object []) convertGVariantToJava(gvar_parameters);
+ if (nameArray [0] != null && nameArray[0] instanceof String) EXTENSION_DBUS_NAME = (String) nameArray[0];
+ if (nameArray [1] != null && nameArray[1] instanceof String) EXTENSION_DBUS_PATH = (String) nameArray[1];
+ proxyToExtension = proxyToExtensionInit();
+ if (proxyToExtension) {
+ invokeReturnValueExtensionIdentifier(pendingBrowserFunctions, invocation);
+ } else {
+ invokeReturnValueExtensionIdentifier(null, invocation);
+ System.err.println("SWT webkit: proxy to web extension failed to load, BrowserFunction may not work.");
+ }
+ return 0;
+ }
+ } else {
+ result = (String) "SWT webkit: GDBus called an unknown method?";
+ System.err.println("SWT webkit: Received a call from an unknown method: " + java_method_name);
+ }
+ invokeReturnValue(result, invocation);
+ return 0;
+ }
+
+ @SuppressWarnings("unused")
+ private static long /*int*/ callExtensionAsyncCallback (long /*int*/ source_object, long /*int*/ res, long /*int*/ user_data) {
+ long /*int*/[] gerror = new long /*int*/[1];
+ long /*int*/ result = OS.g_dbus_proxy_call_finish (proxy, res, gerror);
+ if (gerror[0] != 0){
+ long /*int*/ errMsg = OS.g_error_get_message(gerror[0]);
+ String msg = Converter.cCharPtrToJavaString(errMsg, false);
+ System.err.println("SWT webkit: There was an error executing something asynchronously with the extension (Java callback).");
+ System.err.println("SWT webkit: the error message provided is " + msg);
+ OS.g_error_free(gerror[0]);
+ }
+ 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 /*int*/ invocation) {
+ long /*int*/ resultGVariant;
+ long /*int*/ builder;
+ long /*int*/ 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 /*int*/ tupleGVariant = convertJavaToGVariant(tupleArray);
+ if (tupleGVariant != 0) {
+ OS.g_variant_builder_add_value(builder, tupleGVariant);
+ } else {
+ System.err.println("SWT webkit: error creating empty BrowserFunction GVariant tuple, skipping.");
}
} else {
- result = (String) "SWT Webkit: Gdbus called an unknown method?";
- System.err.println("SWT WebkitGDBus: Received a call from an unknown method: " + java_method_name);
+ 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 webkit: String array with BrowserFunction and URL should never have"
+ + "more than 2 Strings");
+ }
+ tupleArray[0] = id;
+ System.arraycopy(stringArray, 0, tupleArray, 1, 2);
+ long /*int*/ tupleGVariant = convertJavaToGVariant(tupleArray);
+ if (tupleGVariant != 0) {
+ OS.g_variant_builder_add_value(builder, tupleGVariant);
+ } else {
+ System.err.println("SWT webkit: 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("An error packaging the GVariant occurred: type mismatch.");
}
+ long /*int*/ [] variants = {resultGVariant};
+ long /*int*/ finalGVariant = OS.g_variant_new_tuple(variants, 1);
+ OS.g_dbus_method_invocation_return_value(invocation, finalGVariant);
+ OS.g_variant_builder_unref(builder);
+ return;
+ }
+
+ private static void invokeReturnValue (Object result, long /*int*/ invocation) {
long /*int*/ resultGVariant = 0;
try {
resultGVariant = convertJavaToGVariant(new Object [] {result}); // Result has to be a tuple.
@@ -258,10 +393,98 @@ class WebkitGDBus {
resultGVariant = convertJavaToGVariant(new Object [] {errMsg});
}
OS.g_dbus_method_invocation_return_value(invocation, resultGVariant);
- return 0; // void return value.
+ return; // void return value.
}
+ /**
+ * Initializes the proxy connection to the web extension.
+ *
+ * @return true if establishing the proxy connections succeeded,
+ * false otherwise
+ */
+ private static boolean proxyToExtensionInit() {
+ if (proxy != 0) {
+ return true;
+ } else {
+ if (EXTENSION_DBUS_NAME != null && EXTENSION_DBUS_PATH != null) {
+ long /*int*/ [] error = new long /*int*/ [1];
+ byte [] name = Converter.javaStringToCString(EXTENSION_DBUS_NAME);
+ byte [] path = Converter.javaStringToCString(EXTENSION_DBUS_PATH);
+ byte [] interfaceName = Converter.javaStringToCString(EXTENSION_INTERFACE_NAME);
+ proxy = OS.g_dbus_proxy_new_for_bus_sync(OS.G_BUS_TYPE_SESSION, OS.G_DBUS_PROXY_FLAGS_NONE, 0, name, path, interfaceName, 0, error);
+ if (error[0] != 0) {
+ long /*int*/ errMsg = OS.g_error_get_message(error[0]);
+ String msg = Converter.cCharPtrToJavaString(errMsg, false);
+ OS.g_error_free(error[0]);
+ System.err.println("SWT webkit: there was an error establishing the proxy connection to the extension. " +
+ " The error is " + msg);
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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 /*int*/ params, String methodName) {
+ long /*int*/[] gerror = new long /*int*/ [1]; // GError **
+ long /*int*/ gVariant = OS.g_dbus_proxy_call_sync(proxy, Converter.javaStringToCString(methodName),
+ params, OS.G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, 0, gerror);
+ if (gerror[0] != 0) {
+ long /*int*/ errMsg = OS.g_error_get_message(gerror[0]);
+ String msg = Converter.cCharPtrToJavaString(errMsg, false);
+ /*
+ * 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 webkit: There was an error executing something synchronously with the extension.");
+ System.err.println("SWT webkit: The error message is: " + msg);
+ return (Object) false;
+ }
+ OS.g_error_free(gerror[0]);
+ 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 /*int*/ params, String methodName) {
+ long /*int*/[] gerror = new long /*int*/ [1]; // GError **
+ OS.g_dbus_proxy_call(proxy, Converter.javaStringToCString(methodName),
+ params, OS.G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, 0, callExtensionAsyncCallback.getAddress(), gerror);
+ if (gerror[0] != 0) {
+ long /*int*/ errMsg = OS.g_error_get_message(gerror[0]);
+ String msg = Converter.cCharPtrToJavaString(errMsg, false);
+ System.err.println("SWT webkit: There was an error executing something asynchronously with the extension.");
+ System.err.println("SWT webkit: The error message is: " + msg);
+ OS.g_error_free(gerror[0]);
+ return false;
+ }
+ return true;
+ }
/* TYPE NOTES
*
@@ -314,6 +537,10 @@ class WebkitGDBus {
return new Double(OS.g_variant_get_double(gVariant));
}
+ if (OS.g_variant_is_of_type(gVariant, OS.G_VARIANT_TYPE_UINT64)){
+ return new Long(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);
}
@@ -346,6 +573,10 @@ class WebkitGDBus {
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));
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java
index 11ccd2261f..c1df6956a2 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java
@@ -1722,6 +1722,17 @@ public static final long /*int*/ webkit_web_view_get_main_frame (long /*int*/ we
}
/** @method flags=dynamic */
+public static final native long /*int*/ _webkit_web_view_get_page_id (long /*int*/ web_view);
+public static final long /*int*/ webkit_web_view_get_page_id (long /*int*/ web_view) {
+ lock.lock();
+ try {
+ return _webkit_web_view_get_page_id (web_view);
+ } finally {
+ lock.unlock();
+ }
+}
+
+/** @method flags=dynamic */
public static final native double _webkit_web_view_get_progress (long /*int*/ web_view);
public static final double webkit_web_view_get_progress (long /*int*/ web_view) {
assert WEBKIT1 : Webkit1AssertMsg;

Back to the top