[geary/wip/728002-webkit2: 100/105] Clean up JavaScriptCore VAPI, client and engine code.



commit a33654dbd37f95eb27ffe9d59531b7d6b447c2a9
Author: Michael James Gratton <mike vee net>
Date:   Sun Jan 1 15:15:26 2017 +1100

    Clean up JavaScriptCore VAPI, client and engine code.
    
    * bindings/vapi/javascriptcore-4.0.vapi: Make JS objects match their JSC
      definitions: move JSValueFoo methods to JS.Value, etc. Update call
      sites.
    
    * src/client/util/util-webkit.vala: Move WebKit-specific common methods
      from ClientWebView here. Update call sites.
    
    * src/engine/util/util-js.vala: Move JSC-specific common methods from
      ClientWebView and ComposerPageStateTest here. Update call sites.
    
    * src/client/web-process/web-process-extension.vala: Check for and handle
      exceptions when calling JS code.
    
    * src/CMakeLists.txt: Add new source files, make WebKit VAPI generation
      and engine compilation depend on JSC.

 bindings/vapi/javascriptcore-4.0.vapi              |  127 +++++++++++---------
 src/CMakeLists.txt                                 |    5 +-
 src/client/components/client-web-view.vala         |   47 +-------
 src/client/composer/composer-web-view.vala         |    4 +-
 .../conversation-viewer/conversation-web-view.vala |    4 +-
 src/client/util/util-webkit.vala                   |   87 +++++++++++++
 src/client/web-process/web-process-extension.vala  |   60 +++++++--
 src/engine/util/util-js.vala                       |   79 ++++++++++++
 test/js/composer-page-state-test.vala              |   70 ++---------
 9 files changed, 305 insertions(+), 178 deletions(-)
---
diff --git a/bindings/vapi/javascriptcore-4.0.vapi b/bindings/vapi/javascriptcore-4.0.vapi
index 7601d1c..ca65a62 100644
--- a/bindings/vapi/javascriptcore-4.0.vapi
+++ b/bindings/vapi/javascriptcore-4.0.vapi
@@ -7,27 +7,6 @@ namespace JS {
     [SimpleType]
        public struct Context {
 
-        [CCode (cname = "JSValueIsBoolean")]
-        public bool is_boolean(JS.Value value);
-
-        [CCode (cname = "JSValueIsNumber")]
-        public bool is_number(JS.Value value);
-
-        [CCode (cname = "JSValueIsObject")]
-        public bool is_object(JS.Value value);
-
-        [CCode (cname = "JSValueToBoolean")]
-        public bool to_boolean(JS.Value value);
-
-        [CCode (cname = "JSValueToNumber")]
-        public double to_number(JS.Value value, out JS.Value exception);
-
-        [CCode (cname = "JSValueToObject")]
-        public Object to_object(JS.Value value, out JS.Value exception);
-
-        [CCode (cname = "JSValueToStringCopy")]
-        public String to_string_copy(JS.Value value, out JS.Value exception);
-
         [CCode (cname = "JSEvaluateScript")]
         public Value evaluate_script(String script,
                                      Object? thisObject,
@@ -35,21 +14,11 @@ namespace JS {
                                      int startingLineNumber,
                                      out Value? exception);
 
-        [CCode (cname = "JSObjectMakeFunction")]
-        public Object make_function(String? name,
-                                    [CCode (array_length_pos=1.5)]
-                                    String[]? parameterNames,
-                                    String body,
-                                    String? sourceURL,
-                                    int startingLineNumber,
-                                    out Value? exception);
-
-        [CCode (cname = "JSObjectCallAsFunction")]
-        public Value call_as_function(Object object,
-                                      Object? thisObject,
-                                      [CCode (array_length_pos=2.5)]
-                                      Value[]? arguments,
-                                      out Value? exception);
+        [CCode (cname = "JSCheckScriptSyntax")]
+        public Value check_script_syntax(String script,
+                                         String? sourceURL,
+                                         int startingLineNumber,
+                                         out Value? exception);
 
        }
 
@@ -61,10 +30,48 @@ namespace JS {
         public bool release();
        }
 
+       [CCode (cname = "JSType", has_type_id = false)]
+       public enum Type {
+
+        [CCode (cname = "kJSTypeUndefined")]
+        UNDEFINED,
+
+        [CCode (cname = "kJSTypeNull")]
+        NULL,
+
+        [CCode (cname = "kJSTypeBoolean")]
+        BOOLEAN,
+
+        [CCode (cname = "kJSTypeNumber")]
+        NUMBER,
+
+        [CCode (cname = "kJSTypeString")]
+        STRING,
+
+        [CCode (cname = "kJSTypeObject")]
+        OBJECT
+    }
+
        [CCode (cname = "JSObjectRef")]
     [SimpleType]
        public struct Object {
 
+        [CCode (cname = "JSObjectMakeFunction")]
+        public Object.make_function(String? name,
+                                    [CCode (array_length_pos=1.5)]
+                                    String[]? parameterNames,
+                                    String body,
+                                    String? sourceURL,
+                                    int startingLineNumber,
+                                    out Value? exception);
+
+        [CCode (cname = "JSObjectCallAsFunction", instance_pos = 1.1)]
+        public Value call_as_function(Context ctx,
+                                      Object? thisObject,
+                                      [CCode (array_length_pos=2.5)]
+                                      Value[]? arguments,
+                                      out Value? exception);
+
         [CCode (cname = "JSObjectHasProperty", instance_pos = 1.1)]
         public bool has_property(Context ctx, String property_name);
 
@@ -80,7 +87,31 @@ namespace JS {
        public struct Value {
 
         [CCode (cname = "JSValueGetType", instance_pos = 1.1)]
-        public JS.Type get_type(JS.Context context);
+        public Type get_type(Context context);
+
+        [CCode (cname = "JSValueIsBoolean", instance_pos = 1.1)]
+        public bool is_boolean(Context ctx);
+
+        [CCode (cname = "JSValueIsNumber", instance_pos = 1.1)]
+        public bool is_number(Context ctx);
+
+        [CCode (cname = "JSValueIsObject", instance_pos = 1.1)]
+        public bool is_object(Context ctx);
+
+        [CCode (cname = "JSValueIsString", instance_pos = 1.1)]
+        public bool is_string(Context ctx);
+
+        [CCode (cname = "JSValueToBoolean", instance_pos = 1.1)]
+        public bool to_boolean(Context ctx);
+
+        [CCode (cname = "JSValueToNumber", instance_pos = 1.1)]
+        public double to_number(Context ctx, out Value exception);
+
+        [CCode (cname = "JSValueToObject", instance_pos = 1.1)]
+        public Object to_object(Context ctx, out Value exception);
+
+        [CCode (cname = "JSValueToStringCopy", instance_pos = 1.1)]
+        public String to_string_copy(Context ctx, out Value exception);
 
        }
 
@@ -108,26 +139,4 @@ namespace JS {
 
        }
 
-       [CCode (cname = "JSType", has_type_id = false)]
-       public enum Type {
-
-        [CCode (cname = "kJSTypeUndefined")]
-        UNDEFINED,
-
-        [CCode (cname = "kJSTypeNull")]
-        NULL,
-
-        [CCode (cname = "kJSTypeBoolean")]
-        BOOLEAN,
-
-        [CCode (cname = "kJSTypeNumber")]
-        NUMBER,
-
-        [CCode (cname = "kJSTypeString")]
-        STRING,
-
-        [CCode (cname = "kJSTypeObject")]
-        OBJECT
-    }
-
 }
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cc65d7a..0920201 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -298,6 +298,7 @@ engine/util/util-html.vala
 engine/util/util-imap-utf7.vala
 engine/util/util-inet.vala
 engine/util/util-iterable.vala
+engine/util/util-js.vala
 engine/util/util-numeric.vala
 engine/util/util-object.vala
 engine/util/util-reference-semantics.vala
@@ -404,6 +405,7 @@ client/util/util-gravatar.vala
 client/util/util-gtk.vala
 client/util/util-international.vala
 client/util/util-migrate.vala
+client/util/util-webkit.vala
 )
 
 set(WEB_PROCESS_SRC
@@ -522,6 +524,7 @@ set(ENGINE_PACKAGES
   gio-2.0
   glib-2.0
   gmime-2.6
+  javascriptcore-4.0
   libxml-2.0
   posix
   sqlite3
@@ -533,7 +536,6 @@ set(CLIENT_PACKAGES
   geary-engine
   gio-2.0
   gtk+-3.0
-  javascriptcore-4.0
   libcanberra
   libnotify
   libsecret-1
@@ -645,6 +647,7 @@ add_custom_target(webkit2gtk-vapi
   DEPENDS
     "${CMAKE_BINARY_DIR}/src/webkit2gtk-4.0.vapi"
     "${CMAKE_BINARY_DIR}/src/webkit2gtk-web-extension-4.0.vapi"
+    "${CMAKE_SOURCE_DIR}/bindings/vapi/javascriptcore-4.0.vapi"
 )
 add_custom_command(
     OUTPUT
diff --git a/src/client/components/client-web-view.vala b/src/client/components/client-web-view.vala
index f886cc7..a0564c5 100644
--- a/src/client/components/client-web-view.vala
+++ b/src/client/components/client-web-view.vala
@@ -6,8 +6,6 @@
  * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
-protected errordomain JSError { EXCEPTION, TYPE }
-
 public class ClientWebView : WebKit.WebView {
 
 
@@ -105,43 +103,6 @@ public class ClientWebView : WebKit.WebView {
         );
     }
 
-    protected static bool get_bool_result(WebKit.JavascriptResult result)
-        throws JSError {
-        JS.GlobalContext context = result.get_global_context();
-        JS.Value value = result.get_value();
-        return context.to_boolean(value);
-        // XXX unref result?
-    }
-
-    protected static int get_int_result(WebKit.JavascriptResult result)
-        throws JSError {
-        JS.GlobalContext context = result.get_global_context();
-        JS.Value value = result.get_value();
-        if (!context.is_number(value)) {
-            throw new JSError.TYPE("Value is not a number");
-        }
-        JS.Value? err = null;
-        return (int) context.to_number(value, out err);
-        // XXX check err
-        // XXX unref result?
-    }
-
-    protected static string? get_string_result(WebKit.JavascriptResult result)
-        throws JSError {
-        JS.GlobalContext context = result.get_global_context();
-        JS.Value js_str_value = result.get_value();
-        JS.Value? err = null;
-        JS.String js_str = context.to_string_copy(js_str_value, out err);
-        // XXX check err
-        int len = js_str.get_maximum_utf8_cstring_size();
-        string value = string.nfill(len, 0);
-        js_str.get_utf8_cstring(value, len);
-        js_str.release();
-        debug("Got string: %s", value);
-        return value;
-        // XXX unref result?
-    }
-
     private static inline uint to_wk2_font_size(Pango.FontDescription font) {
         Gdk.Screen? screen = Gdk.Screen.get_default();
         double dpi = screen != null ? screen.get_resolution() : 96.0;
@@ -245,9 +206,9 @@ public class ClientWebView : WebKit.WebView {
         content_manager.script_message_received[PREFERRED_HEIGHT_MESSAGE].connect(
             (result) => {
                 try {
-                    this.preferred_height = get_int_result(result);
+                    this.preferred_height = (int) WebKitUtil.to_number(result);
                     queue_resize();
-                } catch (JSError err) {
+                } catch (Geary.JS.Error err) {
                     debug("Could not get preferred height: %s", err.message);
                 }
             });
@@ -258,8 +219,8 @@ public class ClientWebView : WebKit.WebView {
         content_manager.script_message_received[SELECTION_CHANGED_MESSAGE].connect(
             (result) => {
                 try {
-                    selection_changed(get_bool_result(result));
-                } catch (JSError err) {
+                    selection_changed(WebKitUtil.to_bool(result));
+                } catch (Geary.JS.Error err) {
                     debug("Could not get selection content: %s", err.message);
                 }
             });
diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala
index b156733..457fc5e 100644
--- a/src/client/composer/composer-web-view.vala
+++ b/src/client/composer/composer-web-view.vala
@@ -200,7 +200,7 @@ public class ComposerWebView : ClientWebView {
         WebKit.JavascriptResult result = yield this.run_javascript(
             "geary.getHtml();", null
         );
-        return get_string_result(result);
+        return WebKitUtil.to_string(result);
     }
 
     /**
@@ -210,7 +210,7 @@ public class ComposerWebView : ClientWebView {
         WebKit.JavascriptResult result = yield this.run_javascript(
             "geary.getText();", null
         );
-        return get_string_result(result);
+        return WebKitUtil.to_string(result);
     }
 
     /**
diff --git a/src/client/conversation-viewer/conversation-web-view.vala 
b/src/client/conversation-viewer/conversation-web-view.vala
index a8373cc..03e7f46 100644
--- a/src/client/conversation-viewer/conversation-web-view.vala
+++ b/src/client/conversation-viewer/conversation-web-view.vala
@@ -44,7 +44,7 @@ public class ConversationWebView : ClientWebView {
         WebKit.JavascriptResult result = yield this.run_javascript(
             "geary.getSelectionForFind();", null
         );
-        return get_string_result(result);
+        return WebKitUtil.to_string(result);
     }
 
     /**
@@ -54,7 +54,7 @@ public class ConversationWebView : ClientWebView {
         WebKit.JavascriptResult result = yield this.run_javascript(
             "geary.getSelectionForQuoting();", null
         );
-        return get_string_result(result);
+        return WebKitUtil.to_string(result);
     }
 
 }
diff --git a/src/client/util/util-webkit.vala b/src/client/util/util-webkit.vala
new file mode 100644
index 0000000..cfce6f3
--- /dev/null
+++ b/src/client/util/util-webkit.vala
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 Michael James Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+/**
+ * Utility functions for WebKit objects.
+ */
+namespace WebKitUtil {
+
+    /**
+     * Returns a WebKit {@link WebKit.JavascriptResult} as a `bool`.
+     *
+     * This will raise a {@link Geary.JS.Error.TYPE} error if the
+     * result is not a JavaScript `Boolean`.
+     */
+    public bool to_bool(WebKit.JavascriptResult result)
+        throws Geary.JS.Error {
+        JS.GlobalContext context = result.get_global_context();
+        JS.Value value = result.get_value();
+        if (!value.is_boolean(context)) {
+            throw new Geary.JS.Error.TYPE("Result is not a JS Boolean object");
+        }
+        return value.to_boolean(context);
+    }
+
+    /**
+     * Returns a WebKit {@link WebKit.JavascriptResult} as a `double`.
+     *
+     * This will raise a {@link Geary.JS.Error.TYPE} error if the
+     * result is not a JavaScript `Number`.
+     */
+    public double to_number(WebKit.JavascriptResult result)
+        throws Geary.JS.Error {
+        JS.GlobalContext context = result.get_global_context();
+        JS.Value value = result.get_value();
+        if (!value.is_number(context)) {
+            throw new Geary.JS.Error.TYPE("Result is not a JS Number object");
+        }
+
+        JS.Value? err = null;
+        double number = value.to_number(context, out err);
+        Geary.JS.check_exception(context, err);
+        return number;
+    }
+
+    /**
+     * Returns a WebKit {@link WebKit.JavascriptResult} as a Vala {@link string}.
+     *
+     * This will raise a {@link Geary.JS.Error.TYPE} error if the
+     * result is not a JavaScript `String`.
+     */
+    public string? to_string(WebKit.JavascriptResult result)
+        throws Geary.JS.Error {
+        JS.GlobalContext context = result.get_global_context();
+        JS.Value js_str_value = result.get_value();
+        if (!js_str_value.is_string(context)) {
+            throw new Geary.JS.Error.TYPE("Result is not a JS String object");
+        }
+
+        JS.Value? err = null;
+        JS.String js_str = js_str_value.to_string_copy(context, out err);
+        Geary.JS.check_exception(context, err);
+
+        return Geary.JS.to_string_released(js_str);
+    }
+
+    /**
+     * Converts a WebKit {@link WebKit.JavascriptResult} to a {@link string}.
+     *
+     * Unlike the other `get_foo_result` methods, this will coax the
+     * result to a string, effectively by calling the JavaScript
+     * `toString()` method on it, and returning that value.
+     */
+    public string? as_string(WebKit.JavascriptResult result)
+        throws Geary.JS.Error {
+        JS.GlobalContext context = result.get_global_context();
+        JS.Value js_str_value = result.get_value();
+        JS.Value? err = null;
+        JS.String js_str = js_str_value.to_string_copy(context, out err);
+        Geary.JS.check_exception(context, err);
+        return Geary.JS.to_string_released(js_str);
+    }
+
+}
diff --git a/src/client/web-process/web-process-extension.vala 
b/src/client/web-process/web-process-extension.vala
index 3dbbad1..23728e4 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -81,33 +81,65 @@ public class GearyWebExtension : Object {
     }
 
     private bool should_load_remote_images(WebKit.WebPage page) {
+        bool should_load = false;
         WebKit.Frame frame = page.get_main_frame();
         JS.GlobalContext context = frame.get_javascript_global_context();
-        JS.Value ret = execute_script(context, "geary.allowRemoteImages");
-        // XXX check err here, log it
-        //JS.Value? err;
-        //return context.to_boolean(ret, err);
-        return context.to_boolean(ret);
+        try {
+            JS.Value ret = execute_script(
+                context, "geary.allowRemoteImages", int.parse("__LINE__")
+            );
+            should_load = ret.to_boolean(context);
+        } catch (Error err) {
+            debug(
+                "Error checking PageState::allowRemoteImages: %s",
+                err.message
+            );
+        }
+        return should_load;
     }
 
     private void remote_image_load_blocked(WebKit.WebPage page) {
         WebKit.Frame frame = page.get_main_frame();
-        JS.Context context = frame.get_javascript_global_context();
-        execute_script(context, "geary.remoteImageLoadBlocked();");
+        JS.GlobalContext context = frame.get_javascript_global_context();
+        try {
+            execute_script(
+                context, "geary.remoteImageLoadBlocked();", int.parse("__LINE__")
+            );
+        } catch (Error err) {
+            debug(
+                "Error calling PageState::remoteImageLoadBlocked: %s",
+                err.message
+            );
+        }
     }
 
     private void selection_changed(WebKit.WebPage page) {
         WebKit.Frame frame = page.get_main_frame();
-        JS.Context context = frame.get_javascript_global_context();
-        execute_script(context, "geary.selectionChanged();");
+        JS.GlobalContext context = frame.get_javascript_global_context();
+        try {
+            execute_script(
+                context, "geary.selectionChanged();", int.parse("__LINE__")
+            );
+        } catch (Error err) {
+            debug("Error calling PageStates::selectionChanged: %s", err.message);
+        }
     }
 
-    private JS.Value execute_script(JS.Context context, string script) {
+    private JS.Value execute_script(JS.Context context, string script, int line)
+    throws Geary.JS.Error {
         JS.String js_script = new JS.String.create_with_utf8_cstring(script);
-        // XXX check err here, log it
-        //JS.Value? err;
-        //context.evaluate_script(js_script, null, null, 0, out err);
-        return context.evaluate_script(js_script, null, null, 0, null);
+        JS.String js_source = new JS.String.create_with_utf8_cstring("__FILE__");
+        JS.Value? err = null;
+        try {
+            JS.Value ret = context.evaluate_script(
+                js_script, null, js_source, line, out err
+            );
+            Geary.JS.check_exception(context, err);
+            return ret;
+        } finally {
+            js_script.release();
+            js_source.release();
+        }
     }
 
 }
diff --git a/src/engine/util/util-js.vala b/src/engine/util/util-js.vala
new file mode 100644
index 0000000..ddcf8d4
--- /dev/null
+++ b/src/engine/util/util-js.vala
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 Michael James Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later).  See the COPYING file in this distribution.
+ */
+
+/**
+ * Utility functions for WebKit JavaScriptCore (JSC) objects.
+ */
+namespace Geary.JS {
+
+    /**
+     * Errors produced by functions in {@link Geary.JS}.
+     */
+    public errordomain Error {
+        /**
+         * A JS exception was thrown performing a function call.
+         */
+        EXCEPTION,
+
+        /**
+         * A {@link JS.Value} was not of the expected type.
+         */
+        TYPE
+    }
+
+    /**
+     * Determines if a {@link JS.Value} object is {{{null}}}.
+     *
+     * @return `true` if `js` is `null` or has a {@link JS.Type} of
+     * `NULL` according to `context`.
+     */
+    public inline bool is_null(global::JS.Context context,
+                               global::JS.Value? js) {
+        return (js == null || js.get_type(context) == global::JS.Type.NULL);
+    }
+
+    /**
+     * Returns a JSC {@link JS.String} as a Vala {@link string}.
+     */
+    public inline string to_string_released(global::JS.String js) {
+        int len = js.get_maximum_utf8_cstring_size();
+        string str = string.nfill(len, 0);
+        js.get_utf8_cstring(str, len);
+        js.release();
+        return str;
+    }
+
+    /**
+     * Checks an JS exception returned from a JSC call.
+     *
+     * This method will raise a {@link Geary.JS.Error} if the given
+     * `err_value` is not null (in a Vala or JS sense).
+     */
+    public inline void check_exception(global::JS.Context context,
+                                       global::JS.Value? err_value)
+        throws Error {
+        if (!is_null(context, err_value)) {
+            global::JS.Value? nested_err = null;
+            global::JS.Type err_type = err_value.get_type(context);
+            global::JS.String err_str =
+                err_value.to_string_copy(context, out nested_err);
+
+            if (!is_null(context, nested_err)) {
+                throw new Error.EXCEPTION(
+                    "Nested exception getting exception %s as a string",
+                    err_type.to_string()
+                );
+            }
+
+            throw new Error.EXCEPTION(
+                "JS exception thrown [%s]: %s"
+                .printf(err_type.to_string(), to_string_released(err_str))
+            );
+        }
+    }
+
+}
diff --git a/test/js/composer-page-state-test.vala b/test/js/composer-page-state-test.vala
index 1b8a41c..485c695 100644
--- a/test/js/composer-page-state-test.vala
+++ b/test/js/composer-page-state-test.vala
@@ -41,8 +41,8 @@ class ComposerPageStateTest : Gee.TestCase {
         load_body_fixture(html);
         try {
             assert(run_javascript(@"window.geary.getHtml();") == html + "<br><br>");
-        } catch (JSError err) {
-            print("JSError: %s", err.message);
+        } catch (Geary.JS.Error err) {
+            print("Geary.JS.Error: %s", err.message);
             assert_not_reached();
         } catch (Error err) {
             print("WKError: %s", err.message);
@@ -54,8 +54,8 @@ class ComposerPageStateTest : Gee.TestCase {
         load_body_fixture("<p>para</p>");
         try {
             assert(run_javascript(@"window.geary.getText();") == "para\n\n\n\n\n");
-        } catch (JSError err) {
-            print("JSError: %s", err.message);
+        } catch (Geary.JS.Error err) {
+            print("Geary.JS.Error: %s", err.message);
             assert_not_reached();
         } catch (Error err) {
             print("WKError: %s", err.message);
@@ -68,8 +68,8 @@ class ComposerPageStateTest : Gee.TestCase {
         try {
             assert(run_javascript(@"window.geary.getText();") ==
                    "pre\n\n> quote\n> \npost\n\n\n\n\n");
-        } catch (JSError err) {
-            print("JSError: %s", err.message);
+        } catch (Geary.JS.Error err) {
+            print("Geary.JS.Error: %s", err.message);
             assert_not_reached();
         } catch (Error err) {
             print("WKError: %s", err.message);
@@ -82,8 +82,8 @@ class ComposerPageStateTest : Gee.TestCase {
         try {
             assert(run_javascript(@"window.geary.getText();") ==
                    "pre\n\n> quote1\n> \n>> quote2\n>> \npost\n\n\n\n\n");
-        } catch (JSError err) {
-            print("JSError: %s", err.message);
+        } catch (Geary.JS.Error err) {
+            print("Geary.JS.Error: %s", err.message);
             assert_not_reached();
         } catch (Error err) {
             print("WKError: %s", err.message);
@@ -116,8 +116,8 @@ class ComposerPageStateTest : Gee.TestCase {
                    @"foo\n$(q_marker)quote1\nbar");
             assert(run_javascript(@"ComposerPageState.resolveNesting('$(js_cosy_quote2)', $(js_values));") ==
                    @"foo\n$(q_marker)quote1\n$(q_marker)quote2\nbar");
-        } catch (JSError err) {
-            print("JSError: %s", err.message);
+        } catch (Geary.JS.Error err) {
+            print("Geary.JS.Error: %s", err.message);
             assert_not_reached();
         } catch (Error err) {
             print("WKError: %s", err.message);
@@ -135,8 +135,8 @@ class ComposerPageStateTest : Gee.TestCase {
                    @"$(q_marker)line1");
             assert(run_javascript("ComposerPageState.quoteLines('line1\\nline2');") ==
                    @"$(q_marker)line1\n$(q_marker)line2");
-        } catch (JSError err) {
-            print("JSError: %s", err.message);
+        } catch (Geary.JS.Error err) {
+            print("Geary.JS.Error: %s", err.message);
             assert_not_reached();
         } catch (Error err) {
             print("WKError: %s", err.message);
@@ -158,7 +158,7 @@ class ComposerPageStateTest : Gee.TestCase {
 
         WebKit.JavascriptResult result =
            this.test_view.run_javascript.end(async_result());
-        return get_string_result(result);
+        return WebKitUtil.to_string(result);
     }
 
     protected void async_complete(AsyncResult result) {
@@ -174,48 +174,4 @@ class ComposerPageStateTest : Gee.TestCase {
         return result;
     }
 
-    protected static string? get_string_result(WebKit.JavascriptResult result)
-        throws JSError {
-        JS.GlobalContext context = result.get_global_context();
-        JS.Value js_str_value = result.get_value();
-        JS.Value? err = null;
-        JS.String js_str = context.to_string_copy(js_str_value, out err);
-
-        check_exception(context, err);
-        return to_string_released(js_str);
-    }
-
-    protected static inline void check_exception(JS.Context exe, JS.Value? err_value)
-        throws JSError {
-        if (!is_null(exe, err_value)) {
-            JS.Value? nested_err = null;
-            JS.Type err_type = err_value.get_type(exe);
-            JS.String err_str = exe.to_string_copy(err_value, out nested_err);
-
-            if (!is_null(exe, nested_err)) {
-                throw new JSError.EXCEPTION(
-                    "Nested exception getting exception %s as a string",
-                    err_type.to_string()
-                );
-            }
-
-            throw new JSError.EXCEPTION(
-                "JS exception thrown [%s]: %s"
-                .printf(err_type.to_string(), to_string_released(err_str))
-            );
-        }
-    }
-
-    protected static inline bool is_null(JS.Context exe, JS.Value? js) {
-        return (js == null || js.get_type(exe) == JS.Type.NULL);
-    }
-
-    protected static string to_string_released(JS.String js) {
-        int len = js.get_maximum_utf8_cstring_size();
-        string str = string.nfill(len, 0);
-        js.get_utf8_cstring(str, len);
-        js.release();
-        return str;
-    }
-
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]