summaryrefslogtreecommitdiff
path: root/webhelper/webextensions
diff options
context:
space:
mode:
authorPhilip Chimento <philip@endlessm.com>2015-05-19 11:31:25 -0700
committerPhilip Chimento <philip@endlessm.com>2015-05-21 13:22:23 -0700
commita58f5454c8b046ba4f17534a88db1c19ac22b822 (patch)
treeb25eca159bc2d6f54882e9f29cf0818ad15f9c7b /webhelper/webextensions
parente2729f045a9459c9a320c617ac6e599814744e56 (diff)
Expose gettext() to client-side JS
This exposes the function set by webhelper.set_gettext() to the client- side Javascript as a gettext() function, defined on the global window object. This allows apps to translate messages that are generated at runtime, not just messages in static HTML. Some often-used JavaScriptCore operations can be turned into separate functions, which we can put in a separate source file. This is in anticipation of the next commit where we will define another function property of the global object. [endlessm/eos-sdk#291]
Diffstat (limited to 'webhelper/webextensions')
-rw-r--r--webhelper/webextensions/wh2extension.c89
-rw-r--r--webhelper/webextensions/wh2jscutil.c65
-rw-r--r--webhelper/webextensions/wh2jscutil.h25
3 files changed, 179 insertions, 0 deletions
diff --git a/webhelper/webextensions/wh2extension.c b/webhelper/webextensions/wh2extension.c
index c51171e..48ea9aa 100644
--- a/webhelper/webextensions/wh2extension.c
+++ b/webhelper/webextensions/wh2extension.c
@@ -5,9 +5,13 @@
#include <string.h>
#include <glib.h>
+#include <JavaScriptCore/JavaScript.h>
#include <webkit2/webkit-web-extension.h>
#include <webkitdom/webkitdom.h>
+#include "wh2jscutil.h"
+
+#define PRIVATE_NAME "_webhelper_private"
#define WH2_DBUS_INTERFACE_NAME "com.endlessm.WebHelper2.Translation"
#define MAIN_PROGRAM_OBJECT_PATH "/com/endlessm/gettext"
#define MAIN_PROGRAM_INTERFACE_NAME "com.endlessm.WebHelper2.Gettext"
@@ -74,6 +78,51 @@ translation_function (const gchar *message,
return retval;
}
+static JSValueRef
+gettext_shim (JSContextRef js,
+ JSObjectRef function,
+ JSObjectRef this_object,
+ size_t n_args,
+ const JSValueRef args[],
+ JSValueRef *exception)
+{
+ if (n_args != 1)
+ {
+ gchar *errmsg = g_strdup_printf ("Expected one argument to gettext(),"
+ "but got %d.", n_args);
+ *exception = throw_exception (js, errmsg);
+ g_free (errmsg);
+ return NULL;
+ }
+ if (!JSValueIsString (js, args[0]))
+ {
+ *exception = throw_exception (js,
+ "Expected a string argument to gettext().");
+ return NULL;
+ }
+
+ JSObjectRef window = JSContextGetGlobalObject (js);
+ JSStringRef private_name = JSStringCreateWithUTF8CString (PRIVATE_NAME);
+ JSValueRef private_data = JSObjectGetProperty (js, window, private_name,
+ exception);
+ if (JSValueIsUndefined (js, private_data))
+ return NULL; /* propagate exception */
+ Context *ctxt = (Context *) JSObjectGetPrivate ((JSObjectRef) private_data);
+
+ JSStringRef message_ref = JSValueToStringCopy (js, args[0], exception);
+ if (message_ref == NULL)
+ return NULL; /* propagate exception */
+ gchar *message = string_ref_to_string (message_ref);
+ JSStringRelease (message_ref);
+
+ gchar *translation = translation_function (message, ctxt);
+ g_free (message);
+
+ JSValueRef retval = string_to_value_ref (js, translation);
+ g_free (translation);
+ return retval;
+}
+
static void
translate_html (WebKitDOMDocument *dom,
Context *ctxt)
@@ -216,6 +265,41 @@ on_page_created (WebKitWebExtension *extension,
pctxt, NULL);
}
+/* window-object-cleared is the best time to define properties on the page's
+window object, according to the documentation. */
+static void
+on_window_object_cleared (WebKitScriptWorld *script_world,
+ WebKitWebPage *page,
+ WebKitFrame *frame,
+ Context *ctxt)
+{
+ JSGlobalContextRef js =
+ webkit_frame_get_javascript_context_for_script_world (frame, script_world);
+ JSObjectRef window = JSContextGetGlobalObject (js);
+
+ /* First we need to create a custom class for a private data object to store
+ our context in, because you can't pass callback data to JavaScriptCore
+ callbacks. You also can't set private data on a Javascript object if it's not
+ of a custom class, because the built-in classes don't allocate space for a
+ private pointer. */
+ JSClassDefinition class_def = {
+ .className = "PrivateContextObject"
+ };
+ JSClassRef klass = JSClassCreate (&class_def);
+ JSObjectRef private_data = JSObjectMake (js, klass, ctxt);
+ JSClassRelease (klass);
+
+ if (!set_object_property (js, window, PRIVATE_NAME, (JSValueRef) private_data,
+ kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete))
+ return;
+
+ JSObjectRef gettext_func =
+ JSObjectMakeFunctionWithCallback (js, NULL, gettext_shim);
+ if (!set_object_property (js, window, "gettext", (JSValueRef) gettext_func,
+ kJSPropertyAttributeNone))
+ return;
+}
+
static void
on_bus_acquired (GDBusConnection *connection,
const gchar *name,
@@ -225,6 +309,11 @@ on_bus_acquired (GDBusConnection *connection,
ctxt->connection = connection;
+ /* Get a notification when Javascript is ready */
+ WebKitScriptWorld *script_world = webkit_script_world_get_default ();
+ g_signal_connect (script_world, "window-object-cleared",
+ G_CALLBACK (on_window_object_cleared), ctxt);
+
/* Export our interface on the bus */
ctxt->node = g_dbus_node_info_new_for_xml (introspection_xml, &error);
if (ctxt->node == NULL)
diff --git a/webhelper/webextensions/wh2jscutil.c b/webhelper/webextensions/wh2jscutil.c
new file mode 100644
index 0000000..34f325f
--- /dev/null
+++ b/webhelper/webextensions/wh2jscutil.c
@@ -0,0 +1,65 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* Copyright 2015 Endless Mobile, Inc. */
+
+#include <glib.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include "wh2jscutil.h"
+
+G_GNUC_INTERNAL
+gboolean
+set_object_property (JSContextRef js,
+ JSObjectRef object,
+ const gchar *property_name,
+ JSValueRef property_value,
+ JSPropertyAttributes flags)
+{
+ JSValueRef exception = NULL;
+ JSStringRef property_name_ref = JSStringCreateWithUTF8CString (property_name);
+ JSObjectSetProperty (js, object, property_name_ref, property_value, flags,
+ &exception);
+ JSStringRelease (property_name_ref);
+ if (exception != NULL)
+ {
+ g_critical ("There was a problem setting the property '%s'.",
+ property_name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Returns a newly allocated string. */
+G_GNUC_INTERNAL
+gchar *
+string_ref_to_string (JSStringRef string_ref)
+{
+ size_t bufsize = JSStringGetMaximumUTF8CStringSize (string_ref);
+ gchar *string = g_new0 (gchar, bufsize);
+ JSStringGetUTF8CString (string_ref, string, bufsize);
+ return string;
+}
+
+G_GNUC_INTERNAL
+JSValueRef
+string_to_value_ref (JSContextRef js,
+ const gchar *string)
+{
+ JSStringRef string_ref = JSStringCreateWithUTF8CString (string);
+ JSValueRef value_ref = JSValueMakeString (js, string_ref);
+ /* value_ref owns string_ref now */
+ return value_ref;
+}
+
+G_GNUC_INTERNAL
+JSValueRef
+throw_exception (JSContextRef js,
+ const gchar *message)
+{
+ JSValueRef msgval = string_to_value_ref (js, message);
+ JSValueRef inner_error = NULL;
+ JSObjectRef exception = JSObjectMakeError (js, 1, &msgval, &inner_error);
+ if (inner_error != NULL)
+ return inner_error;
+ return (JSValueRef) exception;
+}
diff --git a/webhelper/webextensions/wh2jscutil.h b/webhelper/webextensions/wh2jscutil.h
new file mode 100644
index 0000000..af800e5
--- /dev/null
+++ b/webhelper/webextensions/wh2jscutil.h
@@ -0,0 +1,25 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* Copyright 2015 Endless Mobile, Inc. */
+
+#ifndef WH2_JSC_UTIL_H
+#define WH2_JSC_UTIL_H
+
+#include <glib.h>
+#include <JavaScriptCore/JavaScript.h>
+
+gboolean set_object_property (JSContextRef js,
+ JSObjectRef object,
+ const gchar *property_name,
+ JSValueRef property_value,
+ JSPropertyAttributes flags);
+
+gchar *string_ref_to_string (JSStringRef string_ref);
+
+JSValueRef string_to_value_ref (JSContextRef js,
+ const gchar *string);
+
+JSValueRef throw_exception (JSContextRef js,
+ const gchar *message);
+
+#endif /* WH2_JSC_UTIL_H */