[epiphany/pgriffis/web-extension/api-cleanups: 3/3] WebExtensions: Port all API handlers to json-glib




commit d56fc087474f59d8482c5358f3ca588a101507a0
Author: Patrick Griffis <pgriffis igalia com>
Date:   Sun Jul 3 20:39:54 2022 -0500

    WebExtensions: Port all API handlers to json-glib
    
    Part-of: <https://gitlab.gnome.org/GNOME/epiphany/-/merge_requests/1161>

 src/webextension/api/alarms.c                 | 104 +++----
 src/webextension/api/alarms.h                 |   4 +-
 src/webextension/api/api-utils.c              |  94 ------
 src/webextension/api/api-utils.h              |  20 --
 src/webextension/api/cookies.c                | 127 +++++----
 src/webextension/api/cookies.h                |   4 +-
 src/webextension/api/downloads.c              | 198 ++++++-------
 src/webextension/api/downloads.h              |   4 +-
 src/webextension/api/menus.c                  |  20 +-
 src/webextension/api/menus.h                  |   4 +-
 src/webextension/api/notifications.c          |  68 +++--
 src/webextension/api/notifications.h          |   4 +-
 src/webextension/api/pageaction.c             | 125 ++++----
 src/webextension/api/pageaction.h             |   4 +-
 src/webextension/api/runtime.c                |  73 +++--
 src/webextension/api/runtime.h                |   4 +-
 src/webextension/api/storage.c                | 154 +++++-----
 src/webextension/api/storage.h                |   4 +-
 src/webextension/api/tabs.c                   | 393 ++++++++++++--------------
 src/webextension/api/tabs.h                   |   4 +-
 src/webextension/api/windows.c                | 151 +++++-----
 src/webextension/api/windows.h                |   4 +-
 src/webextension/ephy-json-utils.c            | 199 ++++++++++++-
 src/webextension/ephy-json-utils.h            |  29 ++
 src/webextension/ephy-web-extension-manager.c |  92 +++---
 src/webextension/ephy-web-extension.h         |  18 +-
 src/webextension/meson.build                  |   1 -
 27 files changed, 969 insertions(+), 937 deletions(-)
---
diff --git a/src/webextension/api/alarms.c b/src/webextension/api/alarms.c
index 003026625..0ff26d737 100644
--- a/src/webextension/api/alarms.c
+++ b/src/webextension/api/alarms.c
@@ -23,6 +23,7 @@
 #include <time.h>
 
 #include "ephy-embed-utils.h"
+#include "ephy-json-utils.h"
 #include "ephy-shell.h"
 #include "ephy-window.h"
 
@@ -57,16 +58,6 @@ get_alarms (EphyWebExtension *extension)
   return alarms;
 }
 
-static gdouble
-get_double_property (JSCValue   *object,
-                     const char *name)
-{
-  g_autoptr (JSCValue) value = jsc_value_object_get_property (object, name);
-  if (jsc_value_is_number (value))
-    return jsc_value_to_double (value);
-  return 0.0;
-}
-
 static guint64
 time_now_ms (void)
 {
@@ -164,12 +155,12 @@ on_alarm_start (gpointer user_data)
 
 static void
 alarms_handler_create (EphyWebExtensionSender *sender,
-                        char                   *name,
-                        JSCValue               *args,
-                        GTask                  *task)
+                       const char             *method_name,
+                       JsonArray              *args,
+                       GTask                  *task)
 {
-  g_autoptr (JSCValue) alarm_name = NULL;
-  g_autoptr (JSCValue) alarm_info = NULL;
+  const char *name;
+  JsonObject *alarm_info;
   GHashTable *alarms = get_alarms (sender->extension);
   Alarm *alarm;
   double delay_in_minutes = 0.0;
@@ -178,19 +169,16 @@ alarms_handler_create (EphyWebExtensionSender *sender,
   g_autofree char *name_str = NULL;
 
   /* This takes two optional args, name:str, info:obj */
-  alarm_name = jsc_value_object_get_property_at_index (args, 0);
-  if (jsc_value_is_string (alarm_name)) {
-    name_str = jsc_value_to_string (alarm_name);
-    alarm_info = jsc_value_object_get_property_at_index (args, 1);
-  } else {
-    name_str = g_strdup ("");
-    alarm_info = g_steal_pointer (&alarm_name);
-  }
+  name = ephy_json_array_get_string (args, 0);
+  alarm_info = ephy_json_array_get_object (args, name ? 1 : 0);
+
+  if (!name)
+    name = "";
 
-  if (jsc_value_is_object (alarm_info)) {
-    delay_in_minutes = get_double_property (alarm_info, "delayInMinutes");
-    period_in_minutes = get_double_property (alarm_info, "periodInMinutes");
-    when = get_double_property (alarm_info, "when");
+  if (alarm_info) {
+    delay_in_minutes = ephy_json_object_get_double_with_default (alarm_info, "delayInMinutes", 0.0);
+    period_in_minutes = ephy_json_object_get_double_with_default (alarm_info, "periodInMinutes", 0.0);
+    when = ephy_json_object_get_double_with_default (alarm_info, "when", 0.0);
   }
 
   if (delay_in_minutes && when) {
@@ -201,7 +189,7 @@ alarms_handler_create (EphyWebExtensionSender *sender,
   alarm = g_new0 (Alarm, 1);
   alarm->repeat_interval_ms = minutes_to_ms (period_in_minutes);
   alarm->web_extension = sender->extension;
-  alarm->name = g_steal_pointer (&name_str);
+  alarm->name = g_strdup (name);
 
   if (delay_in_minutes) {
     alarm->timeout_id = g_timeout_add (minutes_to_ms (delay_in_minutes), on_alarm_start, alarm);
@@ -221,20 +209,14 @@ alarms_handler_create (EphyWebExtensionSender *sender,
 
 static void
 alarms_handler_clear (EphyWebExtensionSender *sender,
-                        char                   *name,
-                        JSCValue               *args,
-                        GTask                  *task)
+                      const char             *method_name,
+                      JsonArray              *args,
+                      GTask                  *task)
 {
   GHashTable *alarms = get_alarms (sender->extension);
-  g_autoptr (JSCValue) name_value = jsc_value_object_get_property_at_index (args, 0);
-  g_autofree char *name_str = NULL;
+  const char *name = ephy_json_array_get_string_with_default (args, 0, "");
 
-  if (!jsc_value_is_string (name_value))
-    name_str = g_strdup ("");
-  else
-    name_str = jsc_value_to_string (name_value);
-
-  if (g_hash_table_remove (alarms, name_str)) {
+  if (g_hash_table_remove (alarms, name)) {
     g_task_return_pointer (task, g_strdup ("true"), g_free);
     return;
   }
@@ -244,9 +226,9 @@ alarms_handler_clear (EphyWebExtensionSender *sender,
 
 static void
 alarms_handler_clear_all (EphyWebExtensionSender *sender,
-                        char                   *name,
-                        JSCValue               *args,
-                        GTask                  *task)
+                          const char             *method_name,
+                          JsonArray              *args,
+                          GTask                  *task)
 {
   GHashTable *alarms = get_alarms (sender->extension);
 
@@ -261,28 +243,30 @@ alarms_handler_clear_all (EphyWebExtensionSender *sender,
 
 static void
 alarms_handler_get (EphyWebExtensionSender *sender,
-                    char                   *name,
-                    JSCValue               *args,
+                    const char             *method_name,
+                    JsonArray              *args,
                     GTask                  *task)
 {
   GHashTable *alarms = get_alarms (sender->extension);
-  g_autoptr (JSCValue) name_value = jsc_value_object_get_property_at_index (args, 0);
-  g_autofree char *name_str = NULL;
+  const char *name = ephy_json_array_get_string (args, 0);
   Alarm *alarm;
 
-  if (!jsc_value_is_string (name_value))
-    name_str = g_strdup ("");
-  else
-    name_str = jsc_value_to_string (name_value);
+  if (!name)
+    name = "";
+
+  alarm = g_hash_table_lookup (alarms, name);
+  if (!alarm) {
+    g_task_return_pointer (task, NULL, NULL);
+    return;
+  }
 
-  alarm = g_hash_table_lookup (alarms, name_str);
   g_task_return_pointer (task, alarm_to_json (alarm), g_free);
 }
 
 static void
 alarms_handler_get_all (EphyWebExtensionSender *sender,
-                        char                   *name,
-                        JSCValue               *args,
+                        const char             *method_name,
+                        JsonArray              *args,
                         GTask                  *task)
 {
   GHashTable *alarms = get_alarms (sender->extension);
@@ -298,7 +282,7 @@ alarms_handler_get_all (EphyWebExtensionSender *sender,
   g_task_return_pointer (task, json_to_string (node, FALSE), g_free);
 }
 
-static EphyWebExtensionAsyncApiHandler alarms_handlers[] = {
+static EphyWebExtensionApiHandler alarms_handlers[] = {
   {"clear", alarms_handler_clear},
   {"clearAll", alarms_handler_clear_all},
   {"create", alarms_handler_create},
@@ -308,8 +292,8 @@ static EphyWebExtensionAsyncApiHandler alarms_handlers[] = {
 
 void
 ephy_web_extension_api_alarms_handler (EphyWebExtensionSender *sender,
-                                       char                   *name,
-                                       JSCValue               *args,
+                                       const char             *method_name,
+                                       JsonArray              *args,
                                        GTask                  *task)
 {
   if (!ephy_web_extension_has_permission (sender->extension, "alarms")) {
@@ -318,13 +302,13 @@ ephy_web_extension_api_alarms_handler (EphyWebExtensionSender *sender,
   }
 
   for (guint idx = 0; idx < G_N_ELEMENTS (alarms_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = alarms_handlers[idx];
+    EphyWebExtensionApiHandler handler = alarms_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
 
-  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "alarms.%s(): Not 
Implemented", name);
+  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "alarms.%s(): Not 
Implemented", method_name);
 }
diff --git a/src/webextension/api/alarms.h b/src/webextension/api/alarms.h
index 6e64bee05..162647150 100644
--- a/src/webextension/api/alarms.h
+++ b/src/webextension/api/alarms.h
@@ -28,8 +28,8 @@
 G_BEGIN_DECLS
 
 void ephy_web_extension_api_alarms_handler (EphyWebExtensionSender *sender,
-                                            char                   *name,
-                                            JSCValue               *value,
+                                            const char             *method_name,
+                                            JsonArray              *args,
                                             GTask                  *task);
 
 G_END_DECLS
diff --git a/src/webextension/api/api-utils.h b/src/webextension/api/api-utils.h
index 9b247e2cd..8201ae60a 100644
--- a/src/webextension/api/api-utils.h
+++ b/src/webextension/api/api-utils.h
@@ -20,28 +20,8 @@
 
 #pragma once
 
-#include <jsc/jsc.h>
-
 typedef enum {
   API_VALUE_UNSET = -1,
   API_VALUE_FALSE = 0,
   API_VALUE_TRUE = 1,
 } ApiTriStateValue;
-
-char *                  api_utils_get_string_property                (JSCValue   *obj,
-                                                                      const char *name,
-                                                                      const char *default_value);
-
-gboolean                api_utils_get_boolean_property               (JSCValue   *obj,
-                                                                      const char *name,
-                                                                      gboolean    default_value);
-
-gint32                  api_utils_get_int32_property                 (JSCValue   *obj,
-                                                                      const char *name,
-                                                                      gint32      default_value);
-
-GPtrArray *             api_utils_get_string_array_property          (JSCValue   *obj,
-                                                                      const char *name);
-
-ApiTriStateValue        api_utils_get_tri_state_value_property       (JSCValue   *obj,
-                                                                      const char *name);
\ No newline at end of file
diff --git a/src/webextension/api/cookies.c b/src/webextension/api/cookies.c
index e0f95d8d9..a3badb5e6 100644
--- a/src/webextension/api/cookies.c
+++ b/src/webextension/api/cookies.c
@@ -19,8 +19,10 @@
 
 #include "config.h"
 
-#include "api-utils.h"
+#include "ephy-json-utils.h"
 #include "ephy-shell.h"
+
+#include "api-utils.h"
 #include "cookies.h"
 
 static WebKitCookieManager *
@@ -207,23 +209,23 @@ get_cookies_ready_cb (WebKitCookieManager *cookie_manager,
 
 static void
 cookies_handler_get (EphyWebExtensionSender *sender,
-                     char                   *name,
-                     JSCValue               *args,
+                     const char             *method_name,
+                     JsonArray              *args,
                      GTask                  *task)
 {
-  g_autoptr (JSCValue) details = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *details = ephy_json_array_get_object (args, 0);
   WebKitCookieManager *cookie_manager = get_cookie_manager ();
-  g_autofree char *cookie_name = NULL;
-  g_autofree char *url = NULL;
+  const char *cookie_name;
+  const char *url;
   CookiesCallbackData *callback_data;
 
-  if (!jsc_value_is_object (details)) {
+  if (!details) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"cookies.get(): Missing details object");
     return;
   }
 
-  cookie_name = api_utils_get_string_property (details, "name", NULL);
-  url = api_utils_get_string_property (details, "url", NULL);
+  cookie_name = ephy_json_object_get_string (details, "name");
+  url = ephy_json_object_get_string (details, "url");
 
   if (!url || !cookie_name) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"cookies.get(): details missing url or name");
@@ -237,7 +239,7 @@ cookies_handler_get (EphyWebExtensionSender *sender,
 
   callback_data = g_new0 (CookiesCallbackData, 1);
   callback_data->task = task;
-  callback_data->cookie_name = g_steal_pointer (&cookie_name);
+  callback_data->cookie_name = g_strdup (cookie_name);
 
   /* FIXME: The WebKit API doesn't expose details like first-party URLs to better filter this. The 
underlying libsoup API does so
    * this just requires additions to WebKitGTK. */
@@ -264,40 +266,41 @@ add_cookie_ready_cb (WebKitCookieManager *cookie_manager,
 
 static void
 cookies_handler_set (EphyWebExtensionSender *sender,
-                     char                   *name,
-                     JSCValue               *args,
+                     const char             *method_name,
+                     JsonArray              *args,
                      GTask                  *task)
 {
-  g_autoptr (JSCValue) details = jsc_value_object_get_property_at_index (args, 0);
-  g_autofree char *url = NULL;
-  g_autofree char *domain = NULL;
-  g_autofree char *cookie_name = NULL;
-  g_autofree char *value = NULL;
-  g_autofree char *path = NULL;
-  g_autofree char *same_site_str = NULL;
+  JsonObject *details = ephy_json_array_get_object (args, 0);
+  const char *url;
+  const char *domain;
+  const char *cookie_name;
+  const char *value;
+  const char *path;
+  const char *same_site_str;
   gboolean secure;
   gboolean http_only;
-  gint32 expiration;
+  gint64 expiration;
   g_autoptr (SoupCookie) new_cookie = NULL;
   g_autoptr (GUri) parsed_uri = NULL;
   g_autoptr (GError) error = NULL;
+  g_autoptr (GDateTime) expires_date = NULL;
   WebKitCookieManager *cookie_manager = get_cookie_manager ();
   CookiesCallbackData *callback_data;
 
-  if (!jsc_value_is_object (details)) {
+  if (!details) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"cookies.set(): Missing details object");
     return;
   }
 
-  url = api_utils_get_string_property (details, "url", NULL);
-  domain = api_utils_get_string_property (details, "domain", NULL);
-  cookie_name = api_utils_get_string_property (details, "name", NULL);
-  value = api_utils_get_string_property (details, "value", NULL);
-  path = api_utils_get_string_property (details, "path", NULL);
-  same_site_str = api_utils_get_string_property (details, "sameSite", NULL);
-  expiration = api_utils_get_int32_property (details, "expirationDate", -1);
-  secure = api_utils_get_boolean_property (details, "secure", FALSE);
-  http_only = api_utils_get_boolean_property (details, "httpOnline", FALSE);
+  url = ephy_json_object_get_string (details, "url");
+  domain = ephy_json_object_get_string (details, "domain");
+  cookie_name = ephy_json_object_get_string (details, "name");
+  value = ephy_json_object_get_string (details, "value");
+  path = ephy_json_object_get_string (details, "path");
+  same_site_str = ephy_json_object_get_string (details, "sameSite");
+  expiration = ephy_json_object_get_int (details, "expirationDate");
+  secure = ephy_json_object_get_boolean (details, "secure", FALSE);
+  http_only = ephy_json_object_get_boolean (details, "httpOnline", FALSE);
 
   if (!url) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"cookies.set(): Missing url property");
@@ -319,10 +322,14 @@ cookies_handler_set (EphyWebExtensionSender *sender,
                                 value ? value : "",
                                 domain ? domain : g_uri_get_host (parsed_uri),
                                 path ? path : g_uri_get_path (parsed_uri),
-                                expiration);
+                                -1);
   soup_cookie_set_secure (new_cookie, secure);
   soup_cookie_set_http_only (new_cookie, http_only);
   soup_cookie_set_same_site_policy (new_cookie, string_to_samesite (same_site_str));
+  if (expiration != -1) {
+    expires_date = g_date_time_new_from_unix_local (expiration);
+    soup_cookie_set_expires (new_cookie, expires_date);
+  }
 
   callback_data = g_new0 (CookiesCallbackData, 1);
   callback_data->task = task;
@@ -333,25 +340,25 @@ cookies_handler_set (EphyWebExtensionSender *sender,
 
 static void
 cookies_handler_remove (EphyWebExtensionSender *sender,
-                        char                   *name,
-                        JSCValue               *args,
+                        const char             *method_name,
+                        JsonArray              *args,
                         GTask                  *task)
 {
-  g_autoptr (JSCValue) details = jsc_value_object_get_property_at_index (args, 0);
-  g_autofree char *url = NULL;
-  g_autofree char *cookie_name = NULL;
+  JsonObject *details = ephy_json_array_get_object (args, 0);
+  const char *url;
+  const char *cookie_name;
   WebKitCookieManager *cookie_manager = get_cookie_manager ();
   CookiesCallbackData *callback_data;
 
-  if (!jsc_value_is_object (details)) {
+  if (!details) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"cookies.remove(): Missing details object");
     return;
   }
 
-  url = api_utils_get_string_property (details, "url", NULL);
-  cookie_name = api_utils_get_string_property (details, "name", NULL);
+  url = ephy_json_object_get_string (details, "url");
+  cookie_name = ephy_json_object_get_string (details, "name");
 
-  if (!url || !name) {
+  if (!url || !cookie_name) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"cookies.remove(): Missing url or name property");
     return;
   }
@@ -363,7 +370,7 @@ cookies_handler_remove (EphyWebExtensionSender *sender,
 
   callback_data = g_new0 (CookiesCallbackData, 1);
   callback_data->task = task;
-  callback_data->cookie_name = g_steal_pointer (&cookie_name);
+  callback_data->cookie_name = g_strdup (cookie_name);
   callback_data->remove_after_find = TRUE;
 
   webkit_cookie_manager_get_cookies (cookie_manager, url, NULL, (GAsyncReadyCallback)get_cookies_ready_cb, 
callback_data);
@@ -457,21 +464,21 @@ get_all_cookies_ready_cb (WebKitCookieManager       *cookie_manager,
 
 static void
 cookies_handler_get_all (EphyWebExtensionSender *sender,
-                         char                   *name,
-                         JSCValue               *args,
+                         const char             *method_name,
+                         JsonArray              *args,
                          GTask                  *task)
 {
-  g_autoptr (JSCValue) details = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *details = ephy_json_array_get_object (args, 0);
   WebKitCookieManager *cookie_manager = get_cookie_manager ();
-  g_autofree char *url = NULL;
+  const char *url;
   GetAllCookiesCallbackData *callback_data;
 
-  if (!jsc_value_is_object (details)) {
+  if (!details) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"cookies.getAll(): Missing details object");
     return;
   }
 
-  url = api_utils_get_string_property (details, "url", NULL);
+  url = ephy_json_object_get_string (details, "url");
 
   /* TODO: We can handle the case of no url by using webkit_website_data_manager_fetch() to list all domains 
and then get all cookies
    * for all domains, but this is rather an ugly amount of work compared to libsoup directly. */
@@ -487,11 +494,11 @@ cookies_handler_get_all (EphyWebExtensionSender *sender,
 
   callback_data = g_new0 (GetAllCookiesCallbackData, 1);
   callback_data->task = task;
-  callback_data->name = api_utils_get_string_property (details, "name", NULL);
-  callback_data->domain = api_utils_get_string_property (details, "domain", NULL);
-  callback_data->path = api_utils_get_string_property (details, "path", NULL);
-  callback_data->secure = api_utils_get_tri_state_value_property (details, "secure");
-  callback_data->session = api_utils_get_tri_state_value_property (details, "session");
+  callback_data->name = ephy_json_object_dup_string (details, "name");
+  callback_data->domain = ephy_json_object_dup_string (details, "domain");
+  callback_data->path = ephy_json_object_dup_string (details, "path");
+  callback_data->secure = ephy_json_object_get_boolean (details, "secure", API_VALUE_UNSET);
+  callback_data->session = ephy_json_object_get_boolean (details, "session", API_VALUE_UNSET);
 
   /* FIXME: The WebKit API doesn't expose details like first-party URLs to better filter this. The 
underlying libsoup API does so
    * this just requires additions to WebKitGTK. */
@@ -522,8 +529,8 @@ create_array_of_all_tab_ids (void)
 
 static void
 cookies_handler_get_all_cookie_stores (EphyWebExtensionSender *sender,
-                                       char                   *name,
-                                       JSCValue               *args,
+                                       const char             *method_name,
+                                       JsonArray              *args,
                                        GTask                  *task)
 {
   g_autoptr (JsonBuilder) builder = json_builder_new ();
@@ -546,7 +553,7 @@ cookies_handler_get_all_cookie_stores (EphyWebExtensionSender *sender,
   g_task_return_pointer (task, json_to_string (root, FALSE), g_free);
 }
 
-static EphyWebExtensionAsyncApiHandler cookies_async_handlers[] = {
+static EphyWebExtensionApiHandler cookies_async_handlers[] = {
   {"get", cookies_handler_get},
   {"getAll", cookies_handler_get_all},
   {"getAllCookieStores", cookies_handler_get_all_cookie_stores},
@@ -556,8 +563,8 @@ static EphyWebExtensionAsyncApiHandler cookies_async_handlers[] = {
 
 void
 ephy_web_extension_api_cookies_handler (EphyWebExtensionSender *sender,
-                                        char                   *name,
-                                        JSCValue               *args,
+                                        const char             *method_name,
+                                        JsonArray              *args,
                                         GTask                  *task)
 {
   if (!ephy_web_extension_has_permission (sender->extension, "cookies")) {
@@ -566,10 +573,10 @@ ephy_web_extension_api_cookies_handler (EphyWebExtensionSender *sender,
   }
 
   for (guint idx = 0; idx < G_N_ELEMENTS (cookies_async_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = cookies_async_handlers[idx];
+    EphyWebExtensionApiHandler handler = cookies_async_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
diff --git a/src/webextension/api/cookies.h b/src/webextension/api/cookies.h
index 079fd11ac..2e1ee3e33 100644
--- a/src/webextension/api/cookies.h
+++ b/src/webextension/api/cookies.h
@@ -27,8 +27,8 @@
 G_BEGIN_DECLS
 
 void         ephy_web_extension_api_cookies_handler                    (EphyWebExtensionSender *sender,
-                                                                        char                   *name,
-                                                                        JSCValue               *value,
+                                                                        const char             *method_name,
+                                                                        JsonArray              *args,
                                                                         GTask                  *task);
 
 G_END_DECLS
diff --git a/src/webextension/api/downloads.c b/src/webextension/api/downloads.c
index e3da3ac6e..6046b7a68 100644
--- a/src/webextension/api/downloads.c
+++ b/src/webextension/api/downloads.c
@@ -34,31 +34,31 @@ get_downloads_manager (void)
 
 static void
 downloads_handler_download (EphyWebExtensionSender *sender,
-                            char                   *name,
-                            JSCValue               *args,
+                            const char             *method_name,
+                            JsonArray              *args,
                             GTask                  *task)
 {
-  g_autoptr (JSCValue) options = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *options = ephy_json_array_get_object (args, 0);
   EphyDownloadsManager *downloads_manager = get_downloads_manager ();
   g_autoptr (EphyDownload) download = NULL;
-  g_autofree char *url = NULL;
-  g_autofree char *filename = NULL;
   g_autofree char *suggested_filename = NULL;
   g_autofree char *suggested_directory = NULL;
-  g_autofree char *conflict_action = NULL;
+  const char *url;
+  const char *filename;
+  const char *conflict_action;
 
-  if (!jsc_value_is_object (options)) {
+  if (!options) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.download(): Missing options object");
     return;
   }
 
-  url = api_utils_get_string_property (options, "url", NULL);
+  url = ephy_json_object_get_string (options, "url");
   if (!url) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.download(): Missing url");
     return;
   }
 
-  filename = api_utils_get_string_property (options, "filename", NULL);
+  filename = ephy_json_object_get_string (options, "filename");
   if (filename) {
     g_autoptr (GFile) downloads_dir = g_file_new_for_path (ephy_file_get_downloads_dir ());
     g_autoptr (GFile) destination = g_file_resolve_relative_path (downloads_dir, filename);
@@ -74,13 +74,13 @@ downloads_handler_download (EphyWebExtensionSender *sender,
     suggested_directory = g_file_get_path (parent_dir);
   }
 
-  conflict_action = api_utils_get_string_property (options, "conflictAction", NULL);
+  conflict_action = ephy_json_object_get_string (options, "conflictAction");
 
   download = ephy_download_new_for_uri (url);
   ephy_download_set_allow_overwrite (download, g_strcmp0 (conflict_action, "overwrite") == 0);
   ephy_download_set_choose_filename (download, TRUE);
   ephy_download_set_suggested_destination (download, suggested_directory, suggested_filename);
-  ephy_download_set_always_ask_destination (download, api_utils_get_boolean_property (options, "saveAs", 
FALSE));
+  ephy_download_set_always_ask_destination (download, ephy_json_object_get_boolean (options, "saveAs", 
FALSE));
   ephy_download_set_initiating_web_extension_info (download, ephy_web_extension_get_guid 
(sender->extension), ephy_web_extension_get_name (sender->extension));
   ephy_downloads_manager_add_download (downloads_manager, download);
 
@@ -92,20 +92,20 @@ downloads_handler_download (EphyWebExtensionSender *sender,
 
 static void
 downloads_handler_cancel (EphyWebExtensionSender *sender,
-                          char                   *name,
-                          JSCValue               *args,
+                          const char             *method_name,
+                          JsonArray              *args,
                           GTask                  *task)
 {
-  g_autoptr (JSCValue) download_id = jsc_value_object_get_property_at_index (args, 0);
+  gint64 download_id = ephy_json_array_get_int (args, 0);
   EphyDownloadsManager *downloads_manager = get_downloads_manager ();
   EphyDownload *download;
 
-  if (!jsc_value_is_number (download_id)) {
+  if (download_id < 0) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.cancel(): Missing downloadId");
     return;
   }
 
-  download = ephy_downloads_manager_find_download_by_id (downloads_manager, jsc_value_to_int32 
(download_id));
+  download = ephy_downloads_manager_find_download_by_id (downloads_manager, (guint64)download_id);
   /* If we fail to find one its possible it was removed already. So instead of erroring just consider it a 
success. */
   if (!download) {
     g_task_return_pointer (task, NULL, NULL);
@@ -118,35 +118,35 @@ downloads_handler_cancel (EphyWebExtensionSender *sender,
 
 static void
 downloads_handler_open_or_show (EphyWebExtensionSender *sender,
-                                char                   *name,
-                                JSCValue               *args,
+                                const char             *method_name,
+                                JsonArray              *args,
                                 GTask                  *task)
 {
-  g_autoptr (JSCValue) download_id = jsc_value_object_get_property_at_index (args, 0);
+  gint64 download_id = ephy_json_array_get_int (args, 0);
   EphyDownloadsManager *downloads_manager = get_downloads_manager ();
   EphyDownloadActionType action;
   EphyDownload *download;
 
   /* We reuse this method for both downloads.open() and downloads.show() as they are identical other than 
the action. */
 
-  if (!jsc_value_is_number (download_id)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.%s(): Missing downloadId", name);
+  if (download_id < 0) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.%s(): Missing downloadId", method_name);
     return;
   }
 
-  download = ephy_downloads_manager_find_download_by_id (downloads_manager, jsc_value_to_int32 
(download_id));
+  download = ephy_downloads_manager_find_download_by_id (downloads_manager, (guint64)download_id);
   if (!download) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.%s(): Failed to find downloadId", name);
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.%s(): Failed to find downloadId", method_name);
     return;
   }
 
-  if (strcmp (name, "open") == 0)
+  if (strcmp (method_name, "open") == 0)
     action = EPHY_DOWNLOAD_ACTION_OPEN;
   else
     action = EPHY_DOWNLOAD_ACTION_BROWSE_TO;
 
   if (!ephy_download_do_download_action (download, action)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.%s(): Failed to %s download", name, name);
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.%s(): Failed to %s download", method_name, method_name);
     return;
   }
 
@@ -154,14 +154,17 @@ downloads_handler_open_or_show (EphyWebExtensionSender *sender,
 }
 
 static GDateTime *
-get_download_time_property (JSCValue   *obj,
+get_download_time_property (JsonObject *obj,
                             const char *name)
 {
   /* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/downloads/DownloadTime */
-  g_autoptr (JSCValue) value = jsc_value_object_get_property (obj, name);
+  JsonNode *node = json_object_get_member (obj, name);
 
-  if (jsc_value_is_string (value)) {
-    g_autofree char *string = jsc_value_to_string (value);
+  if (!node || !JSON_NODE_HOLDS_VALUE (node))
+    return NULL;
+
+  if (json_node_get_value_type (node) == G_TYPE_STRING) {
+    const char *string = json_node_get_string (node);
     char *end = NULL;
     guint64 timestamp;
 
@@ -173,10 +176,8 @@ get_download_time_property (JSCValue   *obj,
     return g_date_time_new_from_iso8601 (string, NULL);
   }
 
-  if (jsc_value_is_number (value)) {
-    gint32 timestamp = jsc_value_to_int32 (value);
-    return g_date_time_new_from_unix_local (timestamp);
-  }
+  if (json_node_get_value_type (node) == G_TYPE_INT64)
+    return g_date_time_new_from_unix_local (json_node_get_int (node));
 
   return NULL;
 }
@@ -203,13 +204,13 @@ typedef struct {
   char *url;
   char *content_type;
   char *interrupt_reason;
-  gint32 limit;
-  gint32 id;
-  gint32 bytes_received;
-  gint32 total_bytes;
-  gint32 file_size;
-  gint32 total_bytes_greater;
-  gint32 total_bytes_less;
+  gint64 limit;
+  gint64 id;
+  gint64 bytes_received;
+  gint64 total_bytes;
+  gint64 file_size;
+  gint64 total_bytes_greater;
+  gint64 total_bytes_less;
   DownloadState state;
   ApiTriStateValue paused;
   ApiTriStateValue exists;
@@ -237,28 +238,28 @@ download_query_free (DownloadQuery *query)
 }
 
 static DownloadQuery *
-download_query_new (JSCValue *object)
+download_query_new (JsonObject *object)
 {
   DownloadQuery *query = g_new (DownloadQuery, 1);
-  g_autofree char *danger = NULL;
-  g_autofree char *state = NULL;
-  g_autofree char *mime = NULL;
-
-  query->filename = api_utils_get_string_property (object, "filename", NULL);
-  query->filename_regex = api_utils_get_string_property (object, "filenameRegex", NULL);
-  query->url = api_utils_get_string_property (object, "url", NULL);
-  query->url_regex = api_utils_get_string_property (object, "urlRegex", NULL);
-  query->interrupt_reason = api_utils_get_string_property (object, "error", NULL);
-  mime = api_utils_get_string_property (object, "mime", NULL);
+  const char *danger;
+  const char *state;
+  const char *mime;
+
+  query->filename = ephy_json_object_dup_string (object, "filename");
+  query->filename_regex = ephy_json_object_dup_string (object, "filenameRegex");
+  query->url = ephy_json_object_dup_string (object, "url");
+  query->url_regex = ephy_json_object_dup_string (object, "urlRegex");
+  query->interrupt_reason = ephy_json_object_dup_string (object, "error");
+  mime = ephy_json_object_get_string (object, "mime");
   query->content_type = mime ? g_content_type_from_mime_type (mime) : NULL;
 
-  query->total_bytes_greater = api_utils_get_int32_property (object, "totalBytesGreater", -1);
-  query->total_bytes_less = api_utils_get_int32_property (object, "totalBytesLess", -1);
-  query->limit = api_utils_get_int32_property (object, "limit", -1);
-  query->bytes_received = api_utils_get_int32_property (object, "bytesReceived", -1);
-  query->total_bytes = api_utils_get_int32_property (object, "totalBytes", -1);
-  query->file_size = api_utils_get_int32_property (object, "fileSize", -1);
-  query->id = api_utils_get_int32_property (object, "id", -1);
+  query->total_bytes_greater = ephy_json_object_get_int (object, "totalBytesGreater");
+  query->total_bytes_less = ephy_json_object_get_int (object, "totalBytesLess");
+  query->limit = ephy_json_object_get_int (object, "limit");
+  query->bytes_received = ephy_json_object_get_int (object, "bytesReceived");
+  query->total_bytes = ephy_json_object_get_int (object, "totalBytes");
+  query->file_size = ephy_json_object_get_int (object, "fileSize");
+  query->id = ephy_json_object_get_int (object, "id");
 
   query->start_time = get_download_time_property (object, "startTime");
   query->started_before = get_download_time_property (object, "startedBefore");
@@ -267,19 +268,19 @@ download_query_new (JSCValue *object)
   query->ended_before = get_download_time_property (object, "endedBefore");
   query->ended_after = get_download_time_property (object, "endedAfter");
 
-  query->query = api_utils_get_string_array_property (object, "query");
-  query->order_by = api_utils_get_string_array_property (object, "orderBy");
+  query->query = ephy_json_object_get_string_array (object, "query");
+  query->order_by = ephy_json_object_get_string_array (object, "orderBy");
 
-  query->paused = api_utils_get_tri_state_value_property (object, "paused");
-  query->exists = api_utils_get_tri_state_value_property (object, "exists");
+  query->paused = ephy_json_object_get_boolean (object, "paused", -1);
+  query->exists = ephy_json_object_get_boolean (object, "exists", -1);
 
   /* Epiphany doesn't detect dangerous files so we only care if the query wanted to
    * filter out *safe* files. */
-  danger = api_utils_get_string_property (object, "danger", NULL);
+  danger = ephy_json_object_get_string (object, "danger");
   query->dangerous_only = danger ? strcmp (danger, "safe") != 0 : API_VALUE_UNSET;
 
   query->state = DOWNLOAD_STATE_ANY;
-  state = api_utils_get_string_property (object, "state", NULL);
+  state = ephy_json_object_get_string (object, "state");
   if (state) {
     if (strcmp (state, "in_progress") == 0)
       query->state = DOWNLOAD_STATE_IN_PROGRESS;
@@ -328,8 +329,7 @@ regex_matches (JSCContext *context,
   /* WebExtensions can include arbitrary regex; To match expectations we need to run this against
    * the JavaScript implementation of regex rather than PCREs.
    * Note that this is absolutely untrusted code, however @context is private to this single API call
-   * (created in content_scripts_handle_user_message()) so they cannot actually do anything except
-   * make this match succeed or fail. */
+   * so they cannot actually do anything except make this match succeed or fail. */
   /* FIXME: Maybe this can use `jsc_value_constructor_call()` and `jsc_context_evaluate_in_object()` instead 
of
    * printf to avoid quotes potentially conflicting. */
   g_autofree char *code = g_strdup_printf ("let re = new RegExp('%s'); re.test('%s');", regex, string);
@@ -339,11 +339,11 @@ regex_matches (JSCContext *context,
 
 static gboolean
 matches_filename_or_url (EphyDownload  *download,
-                         DownloadQuery *query,
-                         JSCContext    *context)
+                         DownloadQuery *query)
 {
   g_autofree char *filename = download_get_filename (download);
   const char *url = download_get_url (download);
+  g_autoptr (JSCContext) js_context = NULL;
 
   /* query contains a list of strings that must be in either the URL or the filename.
    * They may also be prefixed with `-` to require negative matches. */
@@ -364,10 +364,13 @@ matches_filename_or_url (EphyDownload  *download,
   if (query->url && g_strcmp0 (query->url, url))
     return FALSE;
 
-  if (query->url_regex && !regex_matches (context, query->url_regex, url))
+  if (query->url_regex || query->filename_regex)
+    js_context = jsc_context_new ();
+
+  if (query->url_regex && !regex_matches (js_context, query->url_regex, url))
     return FALSE;
 
-  if (query->filename_regex && !regex_matches (context, query->filename_regex, filename))
+  if (query->filename_regex && !regex_matches (js_context, query->filename_regex, filename))
     return FALSE;
 
   return TRUE;
@@ -443,8 +446,7 @@ order_downloads (EphyDownload *d1,
 
 static GList *
 filter_downloads (GList         *downloads,
-                  DownloadQuery *query,
-                  JSCContext    *context)
+                  DownloadQuery *query)
 {
   GList *matches = NULL;
   GList *extras = NULL;
@@ -491,7 +493,7 @@ filter_downloads (GList         *downloads,
     if (query->total_bytes_less != -1 && (guint64)query->total_bytes_less > received_size)
       continue;
 
-    if (!matches_filename_or_url (dl, query, context))
+    if (!matches_filename_or_url (dl, query))
       continue;
 
     if (!matches_times (dl, query))
@@ -585,7 +587,7 @@ add_download_to_json (JsonBuilder  *builder,
     json_builder_add_string_value (builder, end_time_iso8601);
   }
   json_builder_set_member_name (builder, "bytesReceived");
-  json_builder_add_int_value (builder, (gint32)download_get_received_size (download));
+  json_builder_add_int_value (builder, (gint64)download_get_received_size (download));
   json_builder_set_member_name (builder, "totalBytes");
   json_builder_add_int_value (builder, -1);
   json_builder_set_member_name (builder, "fileSize");
@@ -617,24 +619,24 @@ download_to_json (EphyDownload *download)
 
 static void
 downloads_handler_search (EphyWebExtensionSender *sender,
-                          char                   *name,
-                          JSCValue               *args,
+                          const char             *method_name,
+                          JsonArray              *args,
                           GTask                  *task)
 {
-  g_autoptr (JSCValue) query_object = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *query_object = ephy_json_array_get_object (args, 0);
   EphyDownloadsManager *downloads_manager = get_downloads_manager ();
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
   DownloadQuery *query;
   GList *downloads;
 
-  if (!jsc_value_is_object (query_object)) {
+  if (!query_object) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.query(): Missing query");
     return;
   }
 
   query = download_query_new (query_object);
-  downloads = filter_downloads (ephy_downloads_manager_get_downloads (downloads_manager), query, 
jsc_value_get_context (args));
+  downloads = filter_downloads (ephy_downloads_manager_get_downloads (downloads_manager), query);
   download_query_free (query);
 
   json_builder_begin_array (builder);
@@ -648,24 +650,24 @@ downloads_handler_search (EphyWebExtensionSender *sender,
 
 static void
 downloads_handler_erase (EphyWebExtensionSender *sender,
-                         char                   *name,
-                         JSCValue               *args,
+                         const char             *method_name,
+                         JsonArray              *args,
                          GTask                  *task)
 {
-  g_autoptr (JSCValue) query_object = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *query_object = ephy_json_array_get_object (args, 0);
   EphyDownloadsManager *downloads_manager = get_downloads_manager ();
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
   DownloadQuery *query;
   GList *downloads;
 
-  if (!jsc_value_is_object (query_object)) {
+  if (!query_object) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.erase(): Missing query");
     return;
   }
 
   query = download_query_new (query_object);
-  downloads = filter_downloads (ephy_downloads_manager_get_downloads (downloads_manager), query, 
jsc_value_get_context (args));
+  downloads = filter_downloads (ephy_downloads_manager_get_downloads (downloads_manager), query);
   download_query_free (query);
 
   json_builder_begin_array (builder);
@@ -683,8 +685,8 @@ downloads_handler_erase (EphyWebExtensionSender *sender,
 
 static void
 downloads_handler_showdefaultfolder (EphyWebExtensionSender *sender,
-                                     char                   *name,
-                                     JSCValue               *args,
+                                     const char             *method_name,
+                                     JsonArray              *args,
                                      GTask                  *task)
 {
   g_autoptr (GFile) default_folder = g_file_new_for_path (ephy_file_get_downloads_dir ());
@@ -712,22 +714,22 @@ delete_file_ready_cb (GFile        *file,
 
 static void
 downloads_handler_removefile (EphyWebExtensionSender *sender,
-                              char                   *name,
-                              JSCValue               *args,
+                              const char             *method_name,
+                              JsonArray              *args,
                               GTask                  *task)
 {
-  g_autoptr (JSCValue) download_id = jsc_value_object_get_property_at_index (args, 0);
+  gint64 download_id = ephy_json_array_get_int (args, 0);
   EphyDownloadsManager *downloads_manager = get_downloads_manager ();
   const char *destination_uri;
   g_autoptr (GFile) destination_file = NULL;
   EphyDownload *download;
 
-  if (!jsc_value_is_number (download_id)) {
+  if (download_id < 0) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.removeFile(): Missing downloadId");
     return;
   }
 
-  download = ephy_downloads_manager_find_download_by_id (downloads_manager, jsc_value_to_int32 
(download_id));
+  download = ephy_downloads_manager_find_download_by_id (downloads_manager, (guint64)download_id);
   if (!download) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"downloads.removeFile(): Failed to find downloadId");
     return;
@@ -747,7 +749,7 @@ downloads_handler_removefile (EphyWebExtensionSender *sender,
   g_file_delete_async (destination_file, G_PRIORITY_DEFAULT, NULL, 
(GAsyncReadyCallback)delete_file_ready_cb, task);
 }
 
-static EphyWebExtensionAsyncApiHandler downloads_async_handlers[] = {
+static EphyWebExtensionApiHandler downloads_async_handlers[] = {
   {"download", downloads_handler_download},
   {"removeFile", downloads_handler_removefile},
   {"cancel", downloads_handler_cancel},
@@ -760,8 +762,8 @@ static EphyWebExtensionAsyncApiHandler downloads_async_handlers[] = {
 
 void
 ephy_web_extension_api_downloads_handler (EphyWebExtensionSender *sender,
-                                          char                   *name,
-                                          JSCValue               *args,
+                                          const char             *method_name,
+                                          JsonArray              *args,
                                           GTask                  *task)
 {
   if (!ephy_web_extension_has_permission (sender->extension, "downloads")) {
@@ -771,15 +773,15 @@ ephy_web_extension_api_downloads_handler (EphyWebExtensionSender *sender,
   }
 
   for (guint idx = 0; idx < G_N_ELEMENTS (downloads_async_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = downloads_async_handlers[idx];
+    EphyWebExtensionApiHandler handler = downloads_async_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
 
-  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "downloads.%s(): 
Not Implemented", name);
+  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "downloads.%s(): 
Not Implemented", method_name);
 }
 
 typedef struct {
diff --git a/src/webextension/api/downloads.h b/src/webextension/api/downloads.h
index 9bb9726cf..395689050 100644
--- a/src/webextension/api/downloads.h
+++ b/src/webextension/api/downloads.h
@@ -33,8 +33,8 @@ void ephy_web_extension_api_downloads_init (EphyWebExtensionManager *manager);
 void ephy_web_extension_api_downloads_dispose (EphyWebExtensionManager *manager);
 
 void ephy_web_extension_api_downloads_handler (EphyWebExtensionSender *sender,
-                                               char                   *name,
-                                               JSCValue               *value,
+                                               const char             *method_name,
+                                               JsonArray              *args,
                                                GTask                  *task);
 
 G_END_DECLS
diff --git a/src/webextension/api/menus.c b/src/webextension/api/menus.c
index 752fbb0f5..c09a63a52 100644
--- a/src/webextension/api/menus.c
+++ b/src/webextension/api/menus.c
@@ -319,7 +319,7 @@ get_menus (EphyWebExtension *extension)
 
 static void
 menus_handler_create (EphyWebExtensionSender *sender,
-                      char                   *name,
+                      const char             *method_name,
                       JsonArray              *args,
                       GTask                  *task)
 {
@@ -373,7 +373,7 @@ menus_remove_by_id (GHashTable *menus,
 
 static void
 menus_handler_remove (EphyWebExtensionSender *sender,
-                      char                   *name,
+                      const char             *method_name,
                       JsonArray              *args,
                       GTask                  *task)
 {
@@ -394,7 +394,7 @@ menus_handler_remove (EphyWebExtensionSender *sender,
 
 static void
 menus_handler_remove_all (EphyWebExtensionSender *sender,
-                          char                   *name,
+                          const char             *method_name,
                           JsonArray              *args,
                           GTask                  *task)
 {
@@ -674,16 +674,10 @@ static EphyWebExtensionApiHandler menus_handlers[] = {
 
 void
 ephy_web_extension_api_menus_handler (EphyWebExtensionSender *sender,
-                                      char                   *name,
-                                      JSCValue               *args,
+                                      const char             *method_name,
+                                      JsonArray              *args,
                                       GTask                  *task)
 {
-  /* Temporary conversion as we migrate to json-glib here. */
-  g_autofree char *json = jsc_value_to_json (args, 0);
-  g_autoptr (JsonNode) json_node = json_from_string (json, NULL);
-  JsonArray *json_args = json_node_get_array (json_node);
-  json_array_seal (json_args);
-
   /* We slightly differ from Firefox here that either permission works for either API but they are 
identical. */
   if (!ephy_web_extension_has_permission (sender->extension, "menus") && !ephy_web_extension_has_permission 
(sender->extension, "contextMenus")) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, "Permission 
Denied");
@@ -693,8 +687,8 @@ ephy_web_extension_api_menus_handler (EphyWebExtensionSender *sender,
   for (guint idx = 0; idx < G_N_ELEMENTS (menus_handlers); idx++) {
     EphyWebExtensionApiHandler handler = menus_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, json_args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
diff --git a/src/webextension/api/menus.h b/src/webextension/api/menus.h
index 22193a0d9..d9d3cb84f 100644
--- a/src/webextension/api/menus.h
+++ b/src/webextension/api/menus.h
@@ -27,8 +27,8 @@
 G_BEGIN_DECLS
 
 void         ephy_web_extension_api_menus_handler                    (EphyWebExtensionSender *sender,
-                                                                      char                   *name,
-                                                                      JSCValue               *value,
+                                                                      const char             *method_name,
+                                                                      JsonArray              *args,
                                                                       GTask                  *task);
 
 WebKitContextMenuItem  *ephy_web_extension_api_menus_create_context_menu        (EphyWebExtension    *self,
diff --git a/src/webextension/api/notifications.c b/src/webextension/api/notifications.c
index a132a5283..a73bdbbd5 100644
--- a/src/webextension/api/notifications.c
+++ b/src/webextension/api/notifications.c
@@ -36,44 +36,43 @@ create_extension_notification_id (EphyWebExtension *web_extension,
 
 static void
 notifications_handler_create (EphyWebExtensionSender *sender,
-                              char                   *name,
-                              JSCValue               *args,
+                              const char             *method_name,
+                              JsonArray              *args,
                               GTask                  *task)
 {
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
-  g_autoptr (JSCValue) button_array = NULL;
   g_autofree char *id = NULL;
   g_autofree char *namespaced_id = NULL;
-  g_autofree char *title = NULL;
-  g_autofree char *message = NULL;
+  JsonObject *notification_options;
+  const char *title;
+  const char *message;
+  JsonArray *buttons;
   g_autoptr (GNotification) notification = NULL;
   const char *extension_guid = ephy_web_extension_get_guid (sender->extension);
 
   /* We share the same "create" and "update" function here because our
    * implementation would be the same. The only difference is we require
    * an id if its for an update. */
-  if (jsc_value_is_string (value)) {
-    id = jsc_value_to_string (value);
-    g_object_unref (value);
-    value = jsc_value_object_get_property_at_index (args, 1);
-  } else {
-    if (strcmp (name, "update") == 0) {
+  id = g_strdup (ephy_json_array_get_string (args, 0));
+  notification_options = ephy_json_array_get_object (args, id ? 1 : 0);
+
+  if (!id) {
+    if (strcmp (method_name, "update") == 0) {
       g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"notifications.update(): id not given");
       return;
     }
     id = g_dbus_generate_guid ();
   }
 
-  if (!jsc_value_is_object (value)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"notifications.%s(): notificationOptions not given", name);
+  if (!notification_options) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"notifications.%s(): notificationOptions not given", method_name);
     return;
   }
 
-  title = api_utils_get_string_property (value, "title", NULL);
-  message = api_utils_get_string_property (value, "message", NULL);
+  title = ephy_json_object_get_string (notification_options, "title");
+  message = ephy_json_object_get_string (notification_options, "message");
 
   if (!title || !message) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"notifications.%s(): title and message are required", name);
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"notifications.%s(): title and message are required", method_name);
     return;
   }
 
@@ -81,11 +80,12 @@ notifications_handler_create (EphyWebExtensionSender *sender,
   g_notification_set_body (notification, message);
   g_notification_set_default_action_and_target (notification, "app.webextension-notification", "(ssi)", 
extension_guid, id, -1);
 
-  button_array = jsc_value_object_get_property (value, "buttons");
-  if (jsc_value_is_array (button_array)) {
+  buttons = ephy_json_object_get_array (notification_options, "buttons");
+  if (buttons) {
+    /* Only max of 2 buttons are supported. */
     for (guint i = 0; i < 2; i++) {
-      g_autoptr (JSCValue) button = jsc_value_object_get_property_at_index (button_array, i);
-      g_autofree char *button_title = api_utils_get_string_property (button, "title", NULL);
+      JsonObject *button = ephy_json_array_get_object (buttons, i);
+      const char *button_title = button ? ephy_json_object_get_string (button, "title") : NULL;
       if (button_title)
         g_notification_add_button_with_target (notification, button_title, "app.webextension-notification", 
"(ssi)", extension_guid, id, i);
     }
@@ -99,20 +99,18 @@ notifications_handler_create (EphyWebExtensionSender *sender,
 
 static void
 notifications_handler_clear (EphyWebExtensionSender *sender,
-                             char                   *name,
-                             JSCValue               *args,
+                             const char             *method_name,
+                             JsonArray              *args,
                              GTask                  *task)
 {
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
-  g_autofree char *id = NULL;
+  const char *id = ephy_json_array_get_string (args, 0);
   g_autofree char *namespaced_id = NULL;
 
-  if (!jsc_value_is_string (value)) {
+  if (!id) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"notifications.clear(): id not given");
     return;
   }
 
-  id = jsc_value_to_string (value);
   namespaced_id = create_extension_notification_id (sender->extension, id);
 
   g_application_withdraw_notification (G_APPLICATION (ephy_shell_get_default ()), namespaced_id);
@@ -123,15 +121,15 @@ notifications_handler_clear (EphyWebExtensionSender *sender,
 
 static void
 notifications_handler_get_all (EphyWebExtensionSender *sender,
-                               char                   *name,
-                               JSCValue               *args,
+                               const char             *method_name,
+                               JsonArray              *args,
                                GTask                  *task)
 {
   /* GNotification does not provide information on the state of notifications. */
   g_task_return_pointer (task, g_strdup ("[]"), g_free);
 }
 
-static EphyWebExtensionAsyncApiHandler notifications_handlers[] = {
+static EphyWebExtensionApiHandler notifications_handlers[] = {
   {"create", notifications_handler_create},
   {"clear", notifications_handler_clear},
   {"getAll", notifications_handler_get_all},
@@ -140,8 +138,8 @@ static EphyWebExtensionAsyncApiHandler notifications_handlers[] = {
 
 void
 ephy_web_extension_api_notifications_handler (EphyWebExtensionSender *sender,
-                                              char                   *name,
-                                              JSCValue               *args,
+                                              const char             *method_name,
+                                              JsonArray              *args,
                                               GTask                  *task)
 {
   if (!ephy_web_extension_has_permission (sender->extension, "notifications")) {
@@ -151,10 +149,10 @@ ephy_web_extension_api_notifications_handler (EphyWebExtensionSender *sender,
   }
 
   for (guint idx = 0; idx < G_N_ELEMENTS (notifications_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = notifications_handlers[idx];
+    EphyWebExtensionApiHandler handler = notifications_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
diff --git a/src/webextension/api/notifications.h b/src/webextension/api/notifications.h
index b368e0699..358559c25 100644
--- a/src/webextension/api/notifications.h
+++ b/src/webextension/api/notifications.h
@@ -26,8 +26,8 @@
 G_BEGIN_DECLS
 
 void ephy_web_extension_api_notifications_handler (EphyWebExtensionSender *sender,
-                                                   char                   *name,
-                                                   JSCValue               *args,
+                                                   const char             *method_name,
+                                                   JsonArray              *args,
                                                    GTask                  *task);
 
 G_END_DECLS
diff --git a/src/webextension/api/pageaction.c b/src/webextension/api/pageaction.c
index e48ba26d1..1e438569b 100644
--- a/src/webextension/api/pageaction.c
+++ b/src/webextension/api/pageaction.c
@@ -27,147 +27,150 @@
 #include "pageaction.h"
 
 static GtkWidget *
-pageaction_get_action (EphyWebExtension *extension,
-                       JSCValue         *value)
+get_action_for_tab_id (EphyWebExtension *extension,
+                       gint64            tab_id)
 {
-  EphyWebView *web_view = NULL;
-  EphyShell *shell = ephy_shell_get_default ();
   EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
-  g_autoptr (JSCValue) tab_id = NULL;
-  gint32 nr;
-
-  if (!jsc_value_is_object (value))
-    return NULL;
+  EphyShell *shell = ephy_shell_get_default ();
+  EphyWebView *web_view;
 
-  tab_id = jsc_value_object_get_property (value, "tabId");
-  if (!jsc_value_is_number (tab_id))
+  if (tab_id <= 0)
     return NULL;
 
-  nr = jsc_value_to_int32 (tab_id);
-  web_view = ephy_shell_get_web_view (shell, nr);
-  if (!web_view) {
-    LOG ("%s(): Invalid tabId '%d', abort\n", __FUNCTION__, nr);
+  web_view = ephy_shell_get_web_view (shell, tab_id);
+  if (!web_view)
     return NULL;
-  }
 
   return ephy_web_extension_manager_get_page_action (manager, extension, web_view);
 }
 
 static void
 pageaction_handler_seticon (EphyWebExtensionSender *sender,
-                            char                   *name,
-                            JSCValue               *args,
+                            const char             *method_name,
+                            JsonArray              *args,
                             GTask                  *task)
 {
+  JsonObject *details = ephy_json_array_get_object (args, 0);
+  gint64 tab_id;
+  const char *path;
   GtkWidget *action;
-  g_autofree char *path_str = NULL;
-  g_autoptr (JSCValue) path_value = NULL;
   g_autoptr (GdkPixbuf) pixbuf = NULL;
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
 
-  action = pageaction_get_action (sender->extension, value);
+  if (!details) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.setIcon(): Missing details object");
+    return;
+  }
+
+  tab_id = ephy_json_object_get_int (details, "tabId");
+  action = get_action_for_tab_id (sender->extension, tab_id);
   if (!action) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.setIcon(): Failed to find action by tabId");
     return;
   }
 
-  path_value = jsc_value_object_get_property (value, "path");
-  if (jsc_value_is_string (path_value)) {
-    path_str = jsc_value_to_string (path_value);
-    pixbuf = ephy_web_extension_load_pixbuf (sender->extension, path_str, -1);
-  } else {
+  if (ephy_json_object_get_object (details, "path")) {
+    /* FIXME: Support object here. */
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.setIcon(): Currently only single path strings are supported.");
     return;
   }
 
+  /* FIXME: path == ""  should reset to default icon and path == NULL should be set to the mainfest's 
page_action icon. */
+  path = ephy_json_object_get_string (details, "path");
+  if (path)
+    pixbuf = ephy_web_extension_load_pixbuf (sender->extension, path, -1);
+
   gtk_image_set_from_pixbuf (GTK_IMAGE (gtk_bin_get_child (GTK_BIN (action))), pixbuf);
   g_task_return_pointer (task, NULL, NULL);
 }
 
 static void
 pageaction_handler_settitle (EphyWebExtensionSender *sender,
-                             char                   *name,
-                             JSCValue               *args,
+                             const char             *method_name,
+                             JsonArray              *args,
                              GTask                  *task)
 {
+  JsonObject *details = ephy_json_array_get_object (args, 0);
+  gint64 tab_id;
+  const char *title;
   GtkWidget *action;
-  g_autoptr (JSCValue) title = NULL;
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
 
-  action = pageaction_get_action (sender->extension, value);
-  if (!action) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+  if (!details) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.setTitle(): Missing details object");
     return;
   }
 
-  title = jsc_value_object_get_property (value, "title");
-  gtk_widget_set_tooltip_text (action, jsc_value_to_string (title));
+  tab_id = ephy_json_object_get_int (details, "tabId");
+  action = get_action_for_tab_id (sender->extension, tab_id);
+  if (!action) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.setTitle(): Failed to find action by tabId");
+    return;
+  }
 
+  /* FIXME: NULL title should set it to the default value in the manifest. */
+  title = ephy_json_object_get_string (details, "title");
+  gtk_widget_set_tooltip_text (action, title);
   g_task_return_pointer (task, NULL, NULL);
 }
 
 static void
 pageaction_handler_gettitle (EphyWebExtensionSender *sender,
-                             char                   *name,
-                             JSCValue               *args,
+                             const char             *method_name,
+                             JsonArray              *args,
                              GTask                  *task)
 {
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
+  gint64 tab_id = ephy_json_array_get_int (args, 0);
   GtkWidget *action;
   g_autofree char *title = NULL;
 
-  action = pageaction_get_action (sender->extension, value);
+  action = get_action_for_tab_id (sender->extension, tab_id);
   if (!action) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.getTitle(): Failed to find action by tabId");
     return;
   }
 
   title = gtk_widget_get_tooltip_text (action);
-
   g_task_return_pointer (task, g_strdup_printf ("\"%s\"", title ? title : ""), g_free);
 }
 
 static void
 pageaction_handler_show (EphyWebExtensionSender *sender,
-                         char                   *name,
-                         JSCValue               *args,
+                         const char             *method_name,
+                         JsonArray              *args,
                          GTask                  *task)
 {
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
+  gint64 tab_id = ephy_json_array_get_int (args, 0);
   GtkWidget *action;
 
-  action = pageaction_get_action (sender->extension, value);
+  action = get_action_for_tab_id (sender->extension, tab_id);
   if (!action) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.show(): Failed to find action by tabId");
     return;
   }
 
   gtk_widget_set_visible (action, TRUE);
-
   g_task_return_pointer (task, NULL, NULL);
 }
 
 static void
 pageaction_handler_hide (EphyWebExtensionSender *sender,
-                         char                   *name,
-                         JSCValue               *args,
+                         const char             *method_name,
+                         JsonArray              *args,
                          GTask                  *task)
 {
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
+  gint64 tab_id = ephy_json_array_get_int (args, 0);
   GtkWidget *action;
 
-  action = pageaction_get_action (sender->extension, value);
+  action = get_action_for_tab_id (sender->extension, tab_id);
   if (!action) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"pageAction.hide(): Failed to find action by tabId");
     return;
   }
 
   gtk_widget_set_visible (action, FALSE);
-
   g_task_return_pointer (task, NULL, NULL);
 }
 
-static EphyWebExtensionAsyncApiHandler pageaction_handlers[] = {
+static EphyWebExtensionApiHandler pageaction_handlers[] = {
   {"setIcon", pageaction_handler_seticon},
   {"setTitle", pageaction_handler_settitle},
   {"getTitle", pageaction_handler_gettitle},
@@ -177,15 +180,15 @@ static EphyWebExtensionAsyncApiHandler pageaction_handlers[] = {
 
 void
 ephy_web_extension_api_pageaction_handler (EphyWebExtensionSender *sender,
-                                           char                   *name,
-                                           JSCValue               *args,
+                                           const char             *method_name,
+                                           JsonArray              *args,
                                            GTask                  *task)
 {
   for (guint idx = 0; idx < G_N_ELEMENTS (pageaction_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = pageaction_handlers[idx];
+    EphyWebExtensionApiHandler handler = pageaction_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
diff --git a/src/webextension/api/pageaction.h b/src/webextension/api/pageaction.h
index 95ec408a0..dfa3c5f7b 100644
--- a/src/webextension/api/pageaction.h
+++ b/src/webextension/api/pageaction.h
@@ -26,8 +26,8 @@
 G_BEGIN_DECLS
 
 void ephy_web_extension_api_pageaction_handler (EphyWebExtensionSender *sender,
-                                                char                   *name,
-                                                JSCValue               *args,
+                                                const char             *method_name,
+                                                JsonArray              *args,
                                                 GTask                  *task);
 
 G_END_DECLS
diff --git a/src/webextension/api/runtime.c b/src/webextension/api/runtime.c
index 2b9e093bd..2c6ceff5d 100644
--- a/src/webextension/api/runtime.c
+++ b/src/webextension/api/runtime.c
@@ -20,17 +20,16 @@
 
 #include "config.h"
 
-#include "runtime.h"
-
-#include "ephy-web-extension-manager.h"
-
 #include "ephy-embed-utils.h"
+#include "ephy-web-extension-manager.h"
 #include "ephy-shell.h"
 
+#include "runtime.h"
+
 static void
 runtime_handler_get_browser_info (EphyWebExtensionSender *sender,
-                                  char                   *name,
-                                  JSCValue               *args,
+                                  const char             *method_name,
+                                  JsonArray              *args,
                                   GTask                  *task)
 {
   g_autoptr (JsonBuilder) builder = json_builder_new ();
@@ -68,8 +67,8 @@ get_arch (void)
 
 static void
 runtime_handler_get_platform_info (EphyWebExtensionSender *sender,
-                                   char                   *name,
-                                   JSCValue               *args,
+                                   const char             *method_name,
+                                   JsonArray              *args,
                                    GTask                  *task)
 {
   g_autoptr (JsonBuilder) builder = json_builder_new ();
@@ -92,48 +91,46 @@ runtime_handler_get_platform_info (EphyWebExtensionSender *sender,
 }
 
 static gboolean
-is_empty_object (JSCValue *value)
+is_empty_object (JsonNode *node)
 {
-  if (jsc_value_is_object (value)) {
-    g_auto (GStrv) keys = jsc_value_object_enumerate_properties (value);
-    return keys == NULL;
-  }
+  g_assert (node);
 
-  return FALSE;
+  if (!JSON_NODE_HOLDS_OBJECT (node))
+    return FALSE;
+
+  return (json_object_get_size (json_node_get_object (node)) == 0);
 }
 
 static void
 runtime_handler_send_message (EphyWebExtensionSender *sender,
-                              char                   *name,
-                              JSCValue               *args,
+                              const char             *method_name,
+                              JsonArray              *args,
                               GTask                  *task)
 {
   EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
-  g_autoptr (GError) error = NULL;
-  g_autoptr (JSCValue) last_value = NULL;
-  g_autoptr (JSCValue) message = NULL;
   g_autofree char *json = NULL;
+  JsonNode *last_arg;
+  JsonNode *message;
 
   /* The arguments of this are so terrible Mozilla dedicates this to describing how to parse it:
    * 
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendMessage#parameters */
-  last_value = jsc_value_object_get_property_at_index (args, 2);
-  if (!jsc_value_is_undefined (last_value)) {
+
+  /* If 3 args were passed the first arg was an extensionId. */
+  if (ephy_json_array_get_element (args, 2)) {
     /* We don't actually support sending to external extensions yet. */
-    error = g_error_new_literal (WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is 
not supported");
-    g_task_return_error (task, g_steal_pointer (&error));
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is 
not supported");
     return;
   }
 
-  last_value = jsc_value_object_get_property_at_index (args, 1);
-  if (jsc_value_is_undefined (last_value) || jsc_value_is_null (last_value) || is_empty_object (last_value)) 
{
-    message = jsc_value_object_get_property_at_index (args, 0);
-  } else {
-    error = g_error_new_literal (WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is 
not supported");
-    g_task_return_error (task, g_steal_pointer (&error));
+  last_arg = ephy_json_array_get_element (args, 1);
+  if (!last_arg || json_node_is_null (last_arg) || is_empty_object (last_arg))
+    message = ephy_json_array_get_element (args, 0);
+  else {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "extensionId is 
not supported");
     return;
   }
 
-  json = jsc_value_to_json (message, 0);
+  json = message ? json_to_string (message, FALSE) : g_strdup ("undefined");
   ephy_web_extension_manager_emit_in_extension_views_with_reply (manager, sender->extension,
                                                                  sender,
                                                                  "runtime.onMessage",
@@ -145,8 +142,8 @@ runtime_handler_send_message (EphyWebExtensionSender *sender,
 
 static void
 runtime_handler_open_options_page (EphyWebExtensionSender *sender,
-                                   char                   *name,
-                                   JSCValue               *args,
+                                   const char             *method_name,
+                                   JsonArray              *args,
                                    GTask                  *task)
 {
   const char *options_ui = ephy_web_extension_get_option_ui_page (sender->extension);
@@ -181,7 +178,7 @@ runtime_handler_open_options_page (EphyWebExtensionSender *sender,
   g_task_return_pointer (task, NULL, NULL);
 }
 
-static EphyWebExtensionAsyncApiHandler runtime_async_handlers[] = {
+static EphyWebExtensionApiHandler runtime_async_handlers[] = {
   {"getBrowserInfo", runtime_handler_get_browser_info},
   {"getPlatformInfo", runtime_handler_get_platform_info},
   {"openOptionsPage", runtime_handler_open_options_page},
@@ -190,15 +187,15 @@ static EphyWebExtensionAsyncApiHandler runtime_async_handlers[] = {
 
 void
 ephy_web_extension_api_runtime_handler (EphyWebExtensionSender *sender,
-                                        char                   *name,
-                                        JSCValue               *args,
+                                        const char             *method_name,
+                                        JsonArray              *args,
                                         GTask                  *task)
 {
   for (guint idx = 0; idx < G_N_ELEMENTS (runtime_async_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = runtime_async_handlers[idx];
+    EphyWebExtensionApiHandler handler = runtime_async_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
diff --git a/src/webextension/api/runtime.h b/src/webextension/api/runtime.h
index 469587732..aafe885f2 100644
--- a/src/webextension/api/runtime.h
+++ b/src/webextension/api/runtime.h
@@ -26,8 +26,8 @@
 G_BEGIN_DECLS
 
 void ephy_web_extension_api_runtime_handler (EphyWebExtensionSender *sender,
-                                             char                   *name,
-                                             JSCValue               *args,
+                                             const char             *method_name,
+                                             JsonArray              *args,
                                              GTask                  *task);
 
 G_END_DECLS
diff --git a/src/webextension/api/storage.c b/src/webextension/api/storage.c
index d84f09e67..db933665e 100644
--- a/src/webextension/api/storage.c
+++ b/src/webextension/api/storage.c
@@ -27,56 +27,25 @@
 
 #include "storage.h"
 
-static JsonNode *
-node_from_value_property (JSCValue   *object,
-                          const char *property)
-{
-  g_autoptr (JSCValue) property_value = jsc_value_object_get_property (object, property);
-  g_autofree char *value_json = jsc_value_to_json (property_value, 0);
-  JsonNode *json = json_from_string (value_json, NULL);
-  g_assert (json);
-  return json;
-}
-
-static GStrv
-strv_from_value (JSCValue *array)
-{
-  g_autoptr (GStrvBuilder) builder = g_strv_builder_new ();
-
-  for (guint i = 0;; i++) {
-    g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (array, i);
-    g_autofree char *str = NULL;
-
-    if (jsc_value_is_undefined (value))
-      break;
-
-    str = jsc_value_to_string (value);
-    g_strv_builder_add (builder, str);
-  }
-
-  return g_strv_builder_end (builder);
-}
-
 static void
 storage_handler_local_set (EphyWebExtensionSender *sender,
-                           char                   *name,
-                           JSCValue               *args,
+                           const char             *method_name,
+                           JsonArray              *args,
                            GTask                  *task)
 {
   JsonNode *local_storage = ephy_web_extension_get_local_storage (sender->extension);
   JsonObject *local_storage_obj = json_node_get_object (local_storage);
-  g_auto (GStrv) keys = NULL;
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *keys = ephy_json_array_get_object (args, 0);
 
-  if (!jsc_value_is_object (value)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+  if (!keys) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"storage.local.set(): Missing keys");
     return;
   }
 
-  keys = jsc_value_object_enumerate_properties (value);
-
-  for (guint i = 0; keys[i]; i++)
-    json_object_set_member (local_storage_obj, keys[i], node_from_value_property (value, keys[i]));
+  for (GList *l = json_object_get_members (keys); l; l = g_list_next (l)) {
+    const char *member_name = l->data;
+    json_object_set_member (local_storage_obj, member_name, json_node_ref (json_object_get_member (keys, 
member_name)));
+  }
 
   /* FIXME: Implement storage.onChanged */
   /* FIXME: Async IO */
@@ -87,17 +56,16 @@ storage_handler_local_set (EphyWebExtensionSender *sender,
 
 static void
 storage_handler_local_get (EphyWebExtensionSender *sender,
-                           char                   *name,
-                           JSCValue               *args,
+                           const char             *method_name,
+                           JsonArray              *args,
                            GTask                  *task)
 {
   JsonNode *local_storage = ephy_web_extension_get_local_storage (sender->extension);
   JsonObject *local_storage_obj = json_node_get_object (local_storage);
+  JsonNode *node = ephy_json_array_get_element (args, 0);
   g_autoptr (JsonBuilder) builder = NULL;
-  g_auto (GStrv) keys = NULL;
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
 
-  if (jsc_value_is_null (value)) {
+  if (!node) {
     g_task_return_pointer (task, json_to_string (local_storage, FALSE), g_free);
     return;
   }
@@ -105,40 +73,52 @@ storage_handler_local_get (EphyWebExtensionSender *sender,
   builder = json_builder_new ();
   json_builder_begin_object (builder);
 
-  if (jsc_value_is_string (value)) {
-    g_autofree char *key = jsc_value_to_string (value);
+  if (ephy_json_node_to_string (node)) {
+    const char *key = ephy_json_node_to_string (node);
     JsonNode *member = json_object_get_member (local_storage_obj, key);
     if (member) {
       json_builder_set_member_name (builder, key);
-      json_builder_add_value (builder, member);
+      json_builder_add_value (builder, json_node_ref (member));
     }
     goto end_get;
   }
 
-  if (jsc_value_is_array (value)) {
-    keys = strv_from_value (value);
-    for (guint i = 0; keys[i]; i++) {
-      const char *key = keys[i];
-      JsonNode *member = json_object_get_member (local_storage_obj, key);
+  if (JSON_NODE_HOLDS_ARRAY (node)) {
+    JsonArray *array = json_node_get_array (node);
+    for (guint i = 0; i < json_array_get_length (array); i++) {
+      const char *key = ephy_json_array_get_string (array, i);
+      JsonNode *member;
+
+      if (!key)
+        continue;
+
+      member = json_object_get_member (local_storage_obj, key);
       if (member) {
         json_builder_set_member_name (builder, key);
-        json_builder_add_value (builder, member);
+        json_builder_add_value (builder, json_node_ref (member));
       }
     }
     goto end_get;
   }
 
-  if (jsc_value_is_object (value)) {
-    keys = jsc_value_object_enumerate_properties (value);
-    for (guint i = 0; keys[i]; i++) {
-      const char *key = keys[i];
-      JsonNode *member = json_object_get_member (local_storage_obj, key);
-      json_builder_set_member_name (builder, key);
-      if (!member)
-        member = node_from_value_property (value, key);
-      json_builder_add_value (builder, member);
+  if (JSON_NODE_HOLDS_OBJECT (node)) {
+    JsonObject *object = json_node_get_object (node);
+    GList *members = json_object_get_members (object);
+    for (GList *l = members; l; l = g_list_next (l)) {
+      const char *member_name = l->data;
+      JsonNode *member;
+
+      /* Either we find the member or we return the value we were given, unless it was undefined. */
+      if (!json_object_has_member (local_storage_obj, member_name))
+        member = json_object_get_member (object, member_name);
+      else
+        member = json_object_get_member (local_storage_obj, member_name);
+
+      if (member) {
+        json_builder_set_member_name (builder, member_name);
+        json_builder_add_value (builder, json_node_ref (member));
+      }
     }
-    goto end_get;
   }
 
 end_get:
@@ -148,28 +128,34 @@ end_get:
 
 static void
 storage_handler_local_remove (EphyWebExtensionSender *sender,
-                              char                   *name,
-                              JSCValue               *args,
+                              const char             *method_name,
+                              JsonArray              *args,
                               GTask                  *task)
 {
   JsonNode *local_storage = ephy_web_extension_get_local_storage (sender->extension);
   JsonObject *local_storage_obj = json_node_get_object (local_storage);
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
+  JsonNode *node = ephy_json_array_get_element (args, 0);
+  const char *string_value;
 
-  if (jsc_value_is_string (value)) {
-    g_autofree char *key = jsc_value_to_string (value);
-    json_object_remove_member (local_storage_obj, key);
+  if (!node)
     goto end_remove;
-  }
 
-  if (jsc_value_is_array (value)) {
-    g_auto (GStrv) keys = strv_from_value (value);
-    for (guint i = 0; keys[i]; i++) {
-      json_object_remove_member (local_storage_obj, keys[i]);
+  if (JSON_NODE_HOLDS_ARRAY (node)) {
+    JsonArray *array = json_node_get_array (node);
+    for (guint i = 0; i < json_array_get_length (array); i++) {
+      const char *name = ephy_json_array_get_string (array, i);
+      if (name)
+        json_object_remove_member (local_storage_obj, name);
     }
     goto end_remove;
   }
 
+  string_value = ephy_json_node_to_string (node);
+  if (string_value) {
+    json_object_remove_member (local_storage_obj, string_value);
+    goto end_remove;
+  }
+
 end_remove:
   ephy_web_extension_save_local_storage (sender->extension);
   g_task_return_pointer (task, NULL, NULL);
@@ -177,8 +163,8 @@ end_remove:
 
 static void
 storage_handler_local_clear (EphyWebExtensionSender *sender,
-                             char                   *name,
-                             JSCValue               *args,
+                             const char             *method_name,
+                             JsonArray              *args,
                              GTask                  *task)
 {
   ephy_web_extension_clear_local_storage (sender->extension);
@@ -186,7 +172,7 @@ storage_handler_local_clear (EphyWebExtensionSender *sender,
   g_task_return_pointer (task, NULL, NULL);
 }
 
-static EphyWebExtensionAsyncApiHandler storage_handlers[] = {
+static EphyWebExtensionApiHandler storage_handlers[] = {
   {"local.set", storage_handler_local_set},
   {"local.get", storage_handler_local_get},
   {"local.remove", storage_handler_local_remove},
@@ -195,8 +181,8 @@ static EphyWebExtensionAsyncApiHandler storage_handlers[] = {
 
 void
 ephy_web_extension_api_storage_handler (EphyWebExtensionSender *sender,
-                                        char                   *name,
-                                        JSCValue               *args,
+                                        const char             *method_name,
+                                        JsonArray              *args,
                                         GTask                  *task)
 {
   if (!ephy_web_extension_has_permission (sender->extension, "storage")) {
@@ -206,13 +192,13 @@ ephy_web_extension_api_storage_handler (EphyWebExtensionSender *sender,
   }
 
   for (guint idx = 0; idx < G_N_ELEMENTS (storage_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = storage_handlers[idx];
+    EphyWebExtensionApiHandler handler = storage_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
 
-  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "storage.%s(): 
Not Implemented", name);
+  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "storage.%s(): 
Not Implemented", method_name);
 }
diff --git a/src/webextension/api/storage.h b/src/webextension/api/storage.h
index ff2b98d0a..86aa9461e 100644
--- a/src/webextension/api/storage.h
+++ b/src/webextension/api/storage.h
@@ -29,8 +29,8 @@
 G_BEGIN_DECLS
 
 void ephy_web_extension_api_storage_handler (EphyWebExtensionSender *sender,
-                                             char                   *name,
-                                             JSCValue               *value,
+                                             const char             *method_name,
+                                             JsonArray              *args,
                                              GTask                  *task);
 
 G_END_DECLS
diff --git a/src/webextension/api/tabs.c b/src/webextension/api/tabs.c
index eaf5d66b3..18edeedde 100644
--- a/src/webextension/api/tabs.c
+++ b/src/webextension/api/tabs.c
@@ -21,9 +21,9 @@
 #include "config.h"
 
 #include "ephy-embed-utils.h"
+#include "ephy-reader-handler.h"
 #include "ephy-shell.h"
 #include "ephy-window.h"
-#include "ephy-reader-handler.h"
 
 #include "api-utils.h"
 #include "tabs.h"
@@ -33,7 +33,7 @@ static const int WINDOW_ID_CURRENT = -2;
 
 static WebKitWebView *
 get_web_view_for_tab_id (EphyShell   *shell,
-                         gint32       tab_id,
+                         gint64       tab_id,
                          EphyWindow **window_out)
 {
   GList *windows;
@@ -61,7 +61,7 @@ get_web_view_for_tab_id (EphyShell   *shell,
     }
   }
 
-  g_debug ("Failed to find tab with id %d", tab_id);
+  g_debug ("Failed to find tab with id %" G_GUINT64_FORMAT, tab_id);
   return NULL;
 }
 
@@ -159,30 +159,30 @@ get_window_by_id (EphyShell *shell,
 
 static void
 tabs_handler_query (EphyWebExtensionSender *sender,
-                    char                   *name,
-                    JSCValue               *args,
+                    const char             *method_name,
+                    JsonArray              *args,
                     GTask                  *task)
 {
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
   EphyShell *shell = ephy_shell_get_default ();
-  g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *query = ephy_json_array_get_object (args, 0);
   GList *windows;
   EphyWindow *active_window;
   ApiTriStateValue current_window;
   ApiTriStateValue active;
-  gint32 window_id;
-  gint32 tab_index;
+  gint64 window_id;
+  gint64 tab_index;
 
-  if (!jsc_value_is_object (value)) {
+  if (!query) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "tabs.query(): 
Missing query object.");
     return;
   }
 
-  active = api_utils_get_tri_state_value_property (value, "active");
-  current_window = api_utils_get_tri_state_value_property (value, "currentWindow");
-  window_id = api_utils_get_int32_property (value, "windowId", -1);
-  tab_index = api_utils_get_int32_property (value, "index", -1);
+  active = ephy_json_object_get_boolean (query, "active", API_VALUE_UNSET);
+  current_window = ephy_json_object_get_boolean (query, "currentWindow", API_VALUE_UNSET);
+  window_id = ephy_json_object_get_int (query, "windowId");
+  tab_index = ephy_json_object_get_int (query, "index");
 
   if (window_id == WINDOW_ID_CURRENT) {
     current_window = TRUE;
@@ -190,7 +190,6 @@ tabs_handler_query (EphyWebExtensionSender *sender,
   }
 
   active_window = EPHY_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (shell)));
-
   windows = gtk_application_get_windows (GTK_APPLICATION (shell));
 
   json_builder_begin_array (builder);
@@ -236,105 +235,122 @@ tabs_handler_query (EphyWebExtensionSender *sender,
 
 static void
 tabs_handler_insert_css (EphyWebExtensionSender *sender,
-                         char                   *name,
-                         JSCValue               *args,
+                         const char             *method_name,
+                         JsonArray              *args,
                          GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
   WebKitUserContentManager *ucm;
   WebKitUserStyleSheet *css = NULL;
-  g_autoptr (JSCValue) code = NULL;
-  g_autoptr (JSCValue) obj = NULL;
-  g_autoptr (JSCValue) tab_id_value = NULL;
+  const char *code;
+  gint64 tab_id = -1;
+  JsonObject *details;
   WebKitWebView *target_web_view;
 
   /* This takes an optional first argument so it's either:
    * [tabId:int, details:obj], or [details:obj] */
-  tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-  if (jsc_value_is_number (tab_id_value)) {
-    obj = jsc_value_object_get_property_at_index (args, 1);
-  } else {
-    obj = g_steal_pointer (&tab_id_value);
-  }
+  details = ephy_json_array_get_object (args, 1);
+  if (!details)
+    details = ephy_json_array_get_object (args, 0);
+  else
+    tab_id = ephy_json_array_get_int (args, 0);
 
-  if (!jsc_value_is_object (obj)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+  if (!details) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.insertCSS(): Missing details");
     return;
   }
 
-  if (!tab_id_value)
+  if (tab_id == -1)
     target_web_view = WEBKIT_WEB_VIEW (ephy_shell_get_active_web_view (shell));
   else
-    target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL);
+    target_web_view = get_web_view_for_tab_id (shell, tab_id, NULL);
 
   if (!target_web_view) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.insertCSS(): Failed to find tabId");
     return;
   }
 
   if (!ephy_web_extension_has_host_or_active_permission (sender->extension, EPHY_WEB_VIEW (target_web_view), 
TRUE)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, "Permission 
Denied");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, 
"tabs.insertCSS(): Permission Denied");
     return;
   }
 
   ucm = webkit_web_view_get_user_content_manager (target_web_view);
 
-  code = jsc_value_object_get_property (obj, "code");
-  css = ephy_web_extension_add_custom_css (sender->extension, jsc_value_to_string (code));
+  /* FIXME: Handle file */
+  if (ephy_json_object_get_string (details, "file")) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.insertCSS(): file is currently unsupported");
+    return;
+  }
 
-  if (css)
-    webkit_user_content_manager_add_style_sheet (ucm, css);
+  code = ephy_json_object_get_string (details, "code");
+  if (!code) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.insertCSS(): Missing code");
+    return;
+  }
+
+  if ((ephy_json_object_get_int (details, "frameId"))) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.insertCSS(): frameId is currently unsupported");
+    return;
+  }
+
+  /* FIXME: Supoprt allFrames and cssOrigin */
+  css = ephy_web_extension_add_custom_css (sender->extension, code);
+  webkit_user_content_manager_add_style_sheet (ucm, css);
 
   g_task_return_pointer (task, NULL, NULL);
 }
 
 static void
 tabs_handler_remove_css (EphyWebExtensionSender *sender,
-                         char                   *name,
-                         JSCValue               *args,
+                         const char             *method_name,
+                         JsonArray              *args,
                          GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
-  JSCValue *code;
+  gint64 tab_id = -1;
+  const char *code;
+  JsonObject *details;
   WebKitUserStyleSheet *css = NULL;
   WebKitUserContentManager *ucm;
-  g_autoptr (JSCValue) obj = NULL;
-  g_autoptr (JSCValue) tab_id_value = NULL;
   WebKitWebView *target_web_view;
 
   /* This takes an optional first argument so it's either:
    * [tabId:int, details:obj], or [details:obj] */
-  tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-  if (jsc_value_is_number (tab_id_value)) {
-    obj = jsc_value_object_get_property_at_index (args, 1);
-  } else {
-    obj = g_steal_pointer (&tab_id_value);
-  }
+  details = ephy_json_array_get_object (args, 1);
+  if (!details)
+    details = ephy_json_array_get_object (args, 0);
+  else
+    tab_id = ephy_json_array_get_int (args, 0);
 
-  if (!jsc_value_is_object (obj)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+  if (!details) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.removeCSS(): Missing details");
     return;
   }
 
-  if (!tab_id_value)
+  if (tab_id == -1)
     target_web_view = WEBKIT_WEB_VIEW (ephy_shell_get_active_web_view (shell));
   else
-    target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL);
+    target_web_view = get_web_view_for_tab_id (shell, tab_id, NULL);
 
   if (!target_web_view) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.removeCSS(): Failed to find tabId");
     return;
   }
 
   if (!ephy_web_extension_has_host_or_active_permission (sender->extension, EPHY_WEB_VIEW (target_web_view), 
TRUE)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, "Permission 
Denied");
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, 
"tabs.removeCSS(): Permission Denied");
     return;
   }
 
   ucm = webkit_web_view_get_user_content_manager (target_web_view);
 
-  code = jsc_value_object_get_property (obj, "code");
-  css = ephy_web_extension_get_custom_css (sender->extension, jsc_value_to_string (code));
+  if (!(code = ephy_json_object_get_string (details, "code"))) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.removeCSS(): Missing code (file is unsupported)");
+    return;
+  }
+
+  css = ephy_web_extension_get_custom_css (sender->extension, code);
   if (css)
     webkit_user_content_manager_remove_style_sheet (ucm, css);
 
@@ -343,24 +359,23 @@ tabs_handler_remove_css (EphyWebExtensionSender *sender,
 
 static void
 tabs_handler_get (EphyWebExtensionSender *sender,
-                  char                   *name,
-                  JSCValue               *args,
+                  const char             *method_name,
+                  JsonArray              *args,
                   GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
-  g_autoptr (JSCValue) tab_id_value = NULL;
+  gint64 tab_id = ephy_json_array_get_int (args, 0);
   EphyWebView *target_web_view;
   EphyWindow *parent_window;
 
-  tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-  if (!jsc_value_is_number (tab_id_value)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+  if (tab_id == -1) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "tabs.get(): 
Missing tabId");
     return;
   }
 
-  target_web_view = EPHY_WEB_VIEW (get_web_view_for_tab_id (shell, jsc_value_to_int32 (args), 
&parent_window));
+  target_web_view = EPHY_WEB_VIEW (get_web_view_for_tab_id (shell, tab_id, &parent_window));
   if (!target_web_view) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
     return;
@@ -395,104 +410,98 @@ on_execute_script_ready (GObject      *source,
 
 static void
 tabs_handler_execute_script (EphyWebExtensionSender *sender,
-                             char                   *name,
-                             JSCValue               *args,
+                             const char             *method_name,
+                             JsonArray              *args,
                              GTask                  *task)
 {
-  g_autoptr (JSCValue) code_value = NULL;
-  g_autoptr (JSCValue) file_value = NULL;
-  g_autoptr (JSCValue) obj = NULL;
-  g_autoptr (JSCValue) tab_id_value = NULL;
-  g_autoptr (GError) error = NULL;
   g_autofree char *code = NULL;
+  const char *file;
+  JsonObject *details;
+  gint64 tab_id = -1;
   EphyShell *shell = ephy_shell_get_default ();
   WebKitWebView *target_web_view;
 
   /* This takes an optional first argument so it's either:
    * [tabId:int, details:obj], or [details:obj] */
-  tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-  if (jsc_value_is_number (tab_id_value)) {
-    obj = jsc_value_object_get_property_at_index (args, 1);
-  } else {
-    obj = g_steal_pointer (&tab_id_value);
-  }
+  details = ephy_json_array_get_object (args, 1);
+  if (!details)
+    details = ephy_json_array_get_object (args, 0);
+  else
+    tab_id = ephy_json_array_get_int (args, 0);
 
-  if (!jsc_value_is_object (obj)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+  if (!details) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.executeScript(): Missing details");
     return;
   }
 
-  file_value = jsc_value_object_get_property (obj, "file");
-  code_value = jsc_value_object_get_property (obj, "code");
+  if ((file = ephy_json_object_get_string (details, "file")))
+    code = ephy_web_extension_get_resource_as_string (sender->extension, file[0] == '/' ? file + 1 : file);
+  else
+    code = ephy_json_object_dup_string (details, "code");
 
-  if (jsc_value_is_string (code_value))
-    code = jsc_value_to_string (code_value);
-  else if (jsc_value_is_string (file_value)) {
-    g_autofree char *resource_path = jsc_value_to_string (file_value);
-    code = ephy_web_extension_get_resource_as_string (sender->extension, resource_path[0] == '/' ? 
resource_path + 1 : resource_path);
-  } else {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "Invalid 
Arguments");
+  if (!code) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.executeScript(): Missing code");
     return;
   }
 
-  if (!tab_id_value)
+  if (tab_id == -1)
     target_web_view = WEBKIT_WEB_VIEW (ephy_shell_get_active_web_view (shell));
   else
-    target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL);
+    target_web_view = get_web_view_for_tab_id (shell, tab_id, NULL);
 
-  if (code && target_web_view) {
-    if (!ephy_web_extension_has_host_or_active_permission (sender->extension, EPHY_WEB_VIEW 
(target_web_view), TRUE)) {
-      g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, "Permission 
Denied");
-      return;
-    }
+  if (!target_web_view) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.executeScript(): Failed to find tabId");
+    return;
+  }
 
-    webkit_web_view_run_javascript_in_world (target_web_view,
-                                             code,
-                                             ephy_web_extension_get_guid (sender->extension),
-                                             NULL,
-                                             on_execute_script_ready,
-                                             task);
+  if (!ephy_web_extension_has_host_or_active_permission (sender->extension, EPHY_WEB_VIEW (target_web_view), 
TRUE)) {
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, "Permission 
Denied");
+    return;
   }
+
+  webkit_web_view_run_javascript_in_world (target_web_view,
+                                           code,
+                                           ephy_web_extension_get_guid (sender->extension),
+                                           NULL,
+                                           on_execute_script_ready,
+                                           task);
 }
 
 static void
 tabs_handler_send_message (EphyWebExtensionSender *sender,
-                           char                   *name,
-                           JSCValue               *args,
+                           const char             *method_name,
+                           JsonArray              *args,
                            GTask                  *task)
 {
   EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
-  g_autoptr (JSCValue) tab_id_value = NULL;
-  g_autoptr (JSCValue) message_value = NULL;
   g_autofree char *serialized_message = NULL;
   EphyShell *shell = ephy_shell_get_default ();
   WebKitWebView *target_web_view;
+  gint64 tab_id = ephy_json_array_get_int (args, 0);
+  JsonNode *message = ephy_json_array_get_element (args, 1);
 
-  tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-  if (!jsc_value_is_number (tab_id_value)) {
+  if (tab_id == -1) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.sendMessage(): Invalid tabId");
     return;
   }
 
-  message_value = jsc_value_object_get_property_at_index (args, 1);
-  if (jsc_value_is_undefined (message_value)) {
+  if (!message) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.sendMessage(): Message argument missing");
     return;
   }
 
-  target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL);
+  target_web_view = get_web_view_for_tab_id (shell, tab_id, NULL);
   if (!target_web_view) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.sendMessage(): Failed to find tabId");
     return;
   }
 
-
   if (!ephy_web_extension_has_host_or_active_permission (sender->extension, EPHY_WEB_VIEW (target_web_view), 
TRUE)) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_PERMISSION_DENIED, 
"tabs.sendMessage(): Permission Denied");
     return;
   }
 
-  serialized_message = jsc_value_to_json (message_value, 0);
+  serialized_message = json_to_string (message, FALSE);
   ephy_web_extension_manager_emit_in_tab_with_reply (manager,
                                                      sender->extension,
                                                      "runtime.onMessage",
@@ -504,16 +513,12 @@ tabs_handler_send_message (EphyWebExtensionSender *sender,
 
 static char *
 resolve_to_absolute_url (EphyWebExtension *web_extension,
-                         char             *url)
+                         const char       *url)
 {
-  char *absolute_url;
-
   if (!url || *url != '/')
-    return url;
+    return g_strdup (url);
 
-  absolute_url = g_strconcat ("ephy-webextension://", ephy_web_extension_get_guid (web_extension), url, 
NULL);
-  g_free (url);
-  return absolute_url;
+  return g_strconcat ("ephy-webextension://", ephy_web_extension_get_guid (web_extension), url, NULL);
 }
 
 gboolean
@@ -547,40 +552,39 @@ ephy_web_extension_api_tabs_url_is_unprivileged (const char *url)
 
 static void
 tabs_handler_create (EphyWebExtensionSender *sender,
-                     char                   *name,
-                     JSCValue               *args,
+                     const char             *method_name,
+                     JsonArray              *args,
                      GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
+  JsonObject *create_properties = ephy_json_array_get_object (args, 0);
   EphyEmbed *embed;
   EphyWindow *parent_window;
   EphyWebView *new_web_view;
-  g_autoptr (JSCValue) create_properties = NULL;
   g_autofree char *url = NULL;
   EphyNewTabFlags new_tab_flags = 0;
   g_autoptr (JsonBuilder) builder = NULL;
   g_autoptr (JsonNode) root = NULL;
 
-  create_properties = jsc_value_object_get_property_at_index (args, 0);
-  if (!jsc_value_is_object (create_properties)) {
+  if (!create_properties) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.create(): First argument is not an object");
     return;
   }
 
-  url = resolve_to_absolute_url (sender->extension, api_utils_get_string_property (create_properties, "url", 
NULL));
+  url = resolve_to_absolute_url (sender->extension, ephy_json_object_get_string (create_properties, "url"));
   if (!ephy_web_extension_api_tabs_url_is_unprivileged (url)) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.create(): URL '%s' is not allowed", url);
     return;
   }
 
-  if (api_utils_get_boolean_property (create_properties, "active", FALSE))
+  if (ephy_json_object_get_boolean (create_properties, "active", FALSE))
     new_tab_flags |= EPHY_NEW_TAB_JUMP;
 
-  parent_window = get_window_by_id (shell, api_utils_get_int32_property (create_properties, "windowId", -1));
+  parent_window = get_window_by_id (shell, ephy_json_object_get_int (create_properties, "windowId"));
   embed = ephy_shell_new_tab (shell, parent_window, NULL, new_tab_flags);
   new_web_view = ephy_embed_get_web_view (embed);
 
-  if (url && api_utils_get_boolean_property (create_properties, "openInReaderMode", FALSE)) {
+  if (url && ephy_json_object_get_boolean (create_properties, "openInReaderMode", FALSE)) {
     char *reader_url = g_strconcat (EPHY_READER_SCHEME, ":", url, NULL);
     g_free (url);
     url = reader_url;
@@ -599,32 +603,28 @@ tabs_handler_create (EphyWebExtensionSender *sender,
 
 static void
 tabs_handler_update (EphyWebExtensionSender *sender,
-                     char                   *name,
-                     JSCValue               *args,
+                     const char             *method_name,
+                     JsonArray              *args,
                      GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
-  g_autoptr (JSCValue) update_properties = NULL;
-  g_autoptr (JSCValue) tab_id_value = NULL;
-  g_autofree char *new_url = NULL;
+  JsonObject *update_properties;
+  gint64 tab_id = -1;
+  const char *new_url;
   g_autoptr (JsonBuilder) builder = NULL;
   g_autoptr (JsonNode) root = NULL;
   WebKitWebView *target_web_view;
   EphyWindow *parent_window;
-  int tab_id = -1;
-  int muted;
+  ApiTriStateValue muted;
 
   /* First arg is optional tabId, second updateProperties. */
-  update_properties = jsc_value_object_get_property_at_index (args, 1);
-  if (jsc_value_is_undefined (update_properties)) {
-    g_object_unref (update_properties);
-    update_properties = jsc_value_object_get_property_at_index (args, 0);
-  } else {
-    tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-    tab_id = jsc_value_to_int32 (tab_id_value);
-  }
+  update_properties = ephy_json_array_get_object (args, 1);
+  if (!update_properties)
+    update_properties = ephy_json_array_get_object (args, 0);
+  else
+    tab_id = ephy_json_array_get_int (args, 0);
 
-  if (!jsc_value_is_object (update_properties)) {
+  if (!update_properties) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.update(): Missing updateProperties.");
     return;
   }
@@ -637,17 +637,17 @@ tabs_handler_update (EphyWebExtensionSender *sender,
   }
 
   if (!target_web_view) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.update(): Failed to find tabId %d.", tab_id);
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.update(): Failed to find tabId %" G_GUINT64_FORMAT, tab_id);
     return;
   }
 
-  new_url = resolve_to_absolute_url (sender->extension, api_utils_get_string_property (update_properties, 
"url", NULL));
+  new_url = resolve_to_absolute_url (sender->extension, ephy_json_object_get_string (update_properties, 
"url"));
   if (!ephy_web_extension_api_tabs_url_is_unprivileged (new_url)) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.update(): URL '%s' is not allowed", new_url);
     return;
   }
 
-  muted = api_utils_get_tri_state_value_property (update_properties, "muted");
+  muted = ephy_json_object_get_boolean (update_properties, "muted", API_VALUE_UNSET);
   if (muted != API_VALUE_UNSET)
     webkit_web_view_set_is_muted (target_web_view, muted);
 
@@ -662,7 +662,7 @@ tabs_handler_update (EphyWebExtensionSender *sender,
 
 static void
 close_tab_id (EphyShell *shell,
-              int        tab_id)
+              gint64     tab_id)
 {
   EphyWindow *parent;
   EphyTabView *tab_view;
@@ -678,33 +678,30 @@ close_tab_id (EphyShell *shell,
 
 static void
 tabs_handler_remove (EphyWebExtensionSender *sender,
-                     char                   *name,
-                     JSCValue               *args,
+                     const char             *method_name,
+                     JsonArray              *args,
                      GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
-  g_autoptr (JSCValue) tab_ids = NULL;
+  JsonNode *tab_ids = ephy_json_array_get_element (args, 0);
+  gint64 tab_id;
 
   /* FIXME: Epiphany should use webkit_web_view_try_close() and this should wait until after that. */
 
-  tab_ids = jsc_value_object_get_property_at_index (args, 0);
+  if (JSON_NODE_HOLDS_ARRAY (tab_ids)) {
+    JsonArray *array = json_node_get_array (tab_ids);
+
+    for (guint i = 0; i < json_array_get_length (array); i++) {
+      gint64 tab_id = ephy_json_array_get_int (array, i);
+      if (tab_id != -1)
+        close_tab_id (shell, tab_id);
+    }
 
-  if (jsc_value_is_number (tab_ids)) {
-    close_tab_id (shell, jsc_value_to_int32 (tab_ids));
     g_task_return_pointer (task, NULL, NULL);
-    return;
   }
 
-  if (jsc_value_is_array (tab_ids)) {
-    g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (tab_ids, 0);
-    for (guint i = 1; !jsc_value_is_undefined (value); i++) {
-      if (jsc_value_is_number (value))
-        close_tab_id (shell, jsc_value_to_int32 (value));
-
-      g_object_unref (value);
-      value = jsc_value_object_get_property_at_index (tab_ids, i);
-    }
-
+  if ((tab_id = ephy_json_node_get_int (tab_ids)) != -1) {
+    close_tab_id (shell, tab_id);
     g_task_return_pointer (task, NULL, NULL);
   }
 
@@ -713,33 +710,22 @@ tabs_handler_remove (EphyWebExtensionSender *sender,
 
 static void
 tabs_handler_set_zoom (EphyWebExtensionSender *sender,
-                       char                   *name,
-                       JSCValue               *args,
+                       const char             *method_name,
+                       JsonArray              *args,
                        GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
-  g_autoptr (JSCValue) zoom_level_value = NULL;
-  g_autoptr (JSCValue) tab_id_value = NULL;
   WebKitWebView *target_web_view;
-  int tab_id = -1;
+  gint64 tab_id = -1;
   double zoom_level;
 
   /* First arg is optional tabId, second zoomFactor. */
-  zoom_level_value = jsc_value_object_get_property_at_index (args, 1);
-  if (jsc_value_is_undefined (zoom_level_value)) {
-    g_object_unref (zoom_level_value);
-    zoom_level_value = jsc_value_object_get_property_at_index (args, 0);
-  } else {
-    tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-    tab_id = jsc_value_to_int32 (tab_id_value);
-  }
-
-  if (!jsc_value_is_number (zoom_level_value)) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.setZoom(): Missing zoomFactor.");
-    return;
-  }
+  zoom_level = ephy_json_array_get_double (args, 1);
+  if (zoom_level == -1.0)
+    zoom_level = ephy_json_array_get_double (args, 0);
+  else
+    tab_id = ephy_json_array_get_int (args, 0);
 
-  zoom_level = jsc_value_to_double (zoom_level_value);
   if (zoom_level < 0.3 || zoom_level > 5) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.setZoom(): zoomFactor must be between 0.3 and 5.0.");
     return;
@@ -751,28 +737,23 @@ tabs_handler_set_zoom (EphyWebExtensionSender *sender,
     target_web_view = WEBKIT_WEB_VIEW (ephy_shell_get_active_web_view (shell));
 
   if (!target_web_view) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.setZoom(): Failed to find tabId %d.", tab_id);
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.setZoom(): Failed to find tabId %" G_GUINT64_FORMAT, tab_id);
     return;
   }
 
-  webkit_web_view_set_zoom_level (target_web_view, jsc_value_to_double (zoom_level_value));
+  webkit_web_view_set_zoom_level (target_web_view, zoom_level);
   g_task_return_pointer (task, NULL, NULL);
 }
 
 static void
 tabs_handler_get_zoom (EphyWebExtensionSender *sender,
-                       char                   *name,
-                       JSCValue               *args,
+                       const char             *method_name,
+                       JsonArray              *args,
                        GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
-  g_autoptr (JSCValue) tab_id_value = NULL;
+  gint64 tab_id = ephy_json_array_get_int (args, 0);
   WebKitWebView *target_web_view;
-  int tab_id = -1;
-
-  tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-  if (jsc_value_is_number (tab_id_value))
-    tab_id = jsc_value_to_int32 (tab_id_value);
 
   if (tab_id >= 0)
     target_web_view = get_web_view_for_tab_id (shell, tab_id, NULL);
@@ -780,7 +761,7 @@ tabs_handler_get_zoom (EphyWebExtensionSender *sender,
     target_web_view = WEBKIT_WEB_VIEW (ephy_shell_get_active_web_view (shell));
 
   if (!target_web_view) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.getZoom(): Failed to find tabId %d.", tab_id);
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.getZoom(): Failed to find tabId %" G_GINT64_FORMAT, tab_id);
     return;
   }
 
@@ -789,18 +770,13 @@ tabs_handler_get_zoom (EphyWebExtensionSender *sender,
 
 static void
 tabs_handler_reload (EphyWebExtensionSender *sender,
-                     char                   *name,
-                     JSCValue               *args,
+                     const char             *method_name,
+                     JsonArray              *args,
                      GTask                  *task)
 {
   EphyShell *shell = ephy_shell_get_default ();
-  g_autoptr (JSCValue) tab_id_value = NULL;
+  gint64 tab_id = ephy_json_array_get_int (args, 0);
   WebKitWebView *target_web_view;
-  int tab_id = -1;
-
-  tab_id_value = jsc_value_object_get_property_at_index (args, 0);
-  if (jsc_value_is_number (tab_id_value))
-    tab_id = jsc_value_to_int32 (tab_id_value);
 
   if (tab_id >= 0)
     target_web_view = get_web_view_for_tab_id (shell, tab_id, NULL);
@@ -808,16 +784,15 @@ tabs_handler_reload (EphyWebExtensionSender *sender,
     target_web_view = WEBKIT_WEB_VIEW (ephy_shell_get_active_web_view (shell));
 
   if (!target_web_view) {
-    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.reload(): Failed to find tabId %d.", tab_id);
+    g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"tabs.reload(): Failed to find tabId %" G_GINT64_FORMAT, tab_id);
     return;
   }
 
   webkit_web_view_reload (WEBKIT_WEB_VIEW (target_web_view));
-
   g_task_return_pointer (task, NULL, NULL);
 }
 
-static EphyWebExtensionAsyncApiHandler tab_async_handlers[] = {
+static EphyWebExtensionApiHandler tab_async_handlers[] = {
   {"executeScript", tabs_handler_execute_script},
   {"sendMessage", tabs_handler_send_message},
   {"create", tabs_handler_create},
@@ -834,19 +809,19 @@ static EphyWebExtensionAsyncApiHandler tab_async_handlers[] = {
 
 void
 ephy_web_extension_api_tabs_handler (EphyWebExtensionSender *sender,
-                                     char                   *name,
-                                     JSCValue               *args,
+                                     const char             *method_name,
+                                     JsonArray              *args,
                                      GTask                  *task)
 {
   for (guint idx = 0; idx < G_N_ELEMENTS (tab_async_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = tab_async_handlers[idx];
+    EphyWebExtensionApiHandler handler = tab_async_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
 
-  g_warning ("%s(): '%s' not implemented by Epiphany!", __FUNCTION__, name);
+  g_warning ("%s(): '%s' not implemented by Epiphany!", __FUNCTION__, method_name);
   g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "Not 
Implemented");
 }
diff --git a/src/webextension/api/tabs.h b/src/webextension/api/tabs.h
index 1ce8ca40c..4793e104b 100644
--- a/src/webextension/api/tabs.h
+++ b/src/webextension/api/tabs.h
@@ -28,8 +28,8 @@
 G_BEGIN_DECLS
 
 void ephy_web_extension_api_tabs_handler (EphyWebExtensionSender *sender,
-                                          char                   *name,
-                                          JSCValue               *value,
+                                          const char             *method_name,
+                                          JsonArray              *args,
                                           GTask                  *task);
 
 JsonNode *ephy_web_extension_api_tabs_create_tab_object (EphyWebExtension *self,
diff --git a/src/webextension/api/windows.c b/src/webextension/api/windows.c
index 850a1d2e4..c2e95d557 100644
--- a/src/webextension/api/windows.c
+++ b/src/webextension/api/windows.c
@@ -21,6 +21,7 @@
 
 #include "ephy-link.h"
 #include "ephy-shell.h"
+
 #include "tabs.h"
 #include "windows.h"
 
@@ -128,34 +129,29 @@ ephy_web_extension_api_windows_create_window_json (EphyWebExtension *extension,
 
 static void
 windows_handler_get (EphyWebExtensionSender *sender,
-                     char                   *name,
-                     JSCValue               *args,
+                     const char             *method_name,
+                     JsonArray              *args,
                      GTask                  *task)
 {
-  g_autoptr (JSCValue) window_id_value = jsc_value_object_get_property_at_index (args, 0);
-  g_autoptr (JSCValue) get_info_value = jsc_value_object_get_property_at_index (args, 1);
+  gint64 window_id = ephy_json_array_get_int (args, 0);
+  JsonObject *get_info = ephy_json_array_get_object (args, 1);
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
   EphyWindow *window;
-  gboolean populate_tabs = FALSE;
+  gboolean populate_tabs;
 
-  if (!jsc_value_is_number (window_id_value)) {
+  if (window_id == -1) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "window.get(): 
First argument is not a windowId");
     return;
   }
 
-  window = ephy_web_extension_api_windows_get_window_for_id (jsc_value_to_int32 (window_id_value));
-
+  populate_tabs = get_info ? ephy_json_object_get_boolean (get_info, "populate", FALSE) : FALSE;
+  window = ephy_web_extension_api_windows_get_window_for_id (window_id);
   if (!window) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, "window.get(): 
Failed to find window by id");
     return;
   }
 
-  if (jsc_value_is_object (get_info_value)) {
-    g_autoptr (JSCValue) populate = jsc_value_object_get_property (get_info_value, "populate");
-    populate_tabs = jsc_value_to_boolean (populate);
-  }
-
   add_window_to_json (sender->extension, builder, window, populate_tabs);
   root = json_builder_get_root (builder);
   g_task_return_pointer (task, json_to_string (root, FALSE), g_free);
@@ -163,12 +159,12 @@ windows_handler_get (EphyWebExtensionSender *sender,
 
 static void
 windows_handler_get_current (EphyWebExtensionSender *sender,
-                             char                   *name,
-                             JSCValue               *args,
+                             const char             *method_name,
+                             JsonArray              *args,
                              GTask                  *task)
 {
   EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
-  g_autoptr (JSCValue) get_info_value = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *get_info = ephy_json_array_get_object (args, 0);
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
   gboolean populate_tabs = FALSE;
@@ -179,10 +175,7 @@ windows_handler_get_current (EphyWebExtensionSender *sender,
   else
     window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sender->view)));
 
-  if (jsc_value_is_object (get_info_value)) {
-    g_autoptr (JSCValue) populate = jsc_value_object_get_property (get_info_value, "populate");
-    populate_tabs = jsc_value_to_boolean (populate);
-  }
+  populate_tabs = get_info ? ephy_json_object_get_boolean (get_info, "populate", FALSE) : FALSE;
 
   add_window_to_json (sender->extension, builder, window, populate_tabs);
   root = json_builder_get_root (builder);
@@ -191,22 +184,18 @@ windows_handler_get_current (EphyWebExtensionSender *sender,
 
 static void
 windows_handler_get_last_focused (EphyWebExtensionSender *sender,
-                                  char                   *name,
-                                  JSCValue               *args,
+                                  const char             *method_name,
+                                  JsonArray              *args,
                                   GTask                  *task)
 {
-  g_autoptr (JSCValue) get_info_value = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *get_info = ephy_json_array_get_object (args, 0);
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
-  gboolean populate_tabs = FALSE;
+  gboolean populate_tabs;
   EphyWindow *window;
 
   window = EPHY_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (ephy_shell_get_default ())));
-
-  if (jsc_value_is_object (get_info_value)) {
-    g_autoptr (JSCValue) populate = jsc_value_object_get_property (get_info_value, "populate");
-    populate_tabs = jsc_value_to_boolean (populate);
-  }
+  populate_tabs = get_info ? ephy_json_object_get_boolean (get_info, "populate", FALSE) : FALSE;
 
   add_window_to_json (sender->extension, builder, window, populate_tabs);
   root = json_builder_get_root (builder);
@@ -215,84 +204,92 @@ windows_handler_get_last_focused (EphyWebExtensionSender *sender,
 
 static void
 windows_handler_get_all (EphyWebExtensionSender *sender,
-                         char                   *name,
-                         JSCValue               *args,
+                         const char             *method_name,
+                         JsonArray              *args,
                          GTask                  *task)
 {
-  g_autoptr (JSCValue) get_info_value = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *get_info = ephy_json_array_get_object (args, 0);
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
-  gboolean populate_tabs = FALSE;
+  gboolean populate_tabs;
   GList *windows;
 
-  if (jsc_value_is_object (get_info_value)) {
-    g_autoptr (JSCValue) populate = jsc_value_object_get_property (get_info_value, "populate");
-    populate_tabs = jsc_value_to_boolean (populate);
-  }
-
-  json_builder_begin_array (builder);
   windows = gtk_application_get_windows (GTK_APPLICATION (ephy_shell_get_default ()));
+  populate_tabs = get_info ? ephy_json_object_get_boolean (get_info, "populate", FALSE) : FALSE;
 
+  json_builder_begin_array (builder);
   for (GList *win_list = windows; win_list; win_list = g_list_next (win_list)) {
     EphyWindow *window = EPHY_WINDOW (win_list->data);
     add_window_to_json (sender->extension, builder, window, populate_tabs);
   }
-
   json_builder_end_array (builder);
 
   root = json_builder_get_root (builder);
   g_task_return_pointer (task, json_to_string (root, FALSE), g_free);
 }
 
+/*
+ * get_url_property:
+ * @object: Object containing urls
+ *
+ * Returns: (transfer container): A #GPtrArray of urls, maybe empty or %NULL if invalid. Strings are owned 
by @object.
+ */
 static GPtrArray *
-get_url_property (JSCValue *object)
+get_url_property (JsonObject *object)
 {
-  g_autoptr (JSCValue) url_value = jsc_value_object_get_property (object, "url");
-  GPtrArray *urls = g_ptr_array_new_full (2, g_free);
+  JsonNode *node = json_object_get_member (object, "url");
+  GPtrArray *urls;
 
-  if (jsc_value_is_undefined (url_value))
-    return urls;
+  if (!node)
+    return NULL;
 
-  if (jsc_value_is_string (url_value)) {
-    g_autofree char *url = jsc_value_to_string (url_value);
-    if (ephy_web_extension_api_tabs_url_is_unprivileged (url))
-      g_ptr_array_add (urls, g_steal_pointer (&url));
-    return urls;
+  if (ephy_json_node_to_string (node)) {
+    const char *url = ephy_json_node_to_string (node);
+
+    if (ephy_web_extension_api_tabs_url_is_unprivileged (url)) {
+      urls = g_ptr_array_sized_new (1);
+      g_ptr_array_add (urls, (char *)url);
+      return urls;
+    }
+
+    return NULL;
   }
 
-  if (jsc_value_is_array (url_value)) {
-    for (guint i = 0; ; i++) {
-      g_autoptr (JSCValue) indexed_value = jsc_value_object_get_property_at_index (url_value, i);
-      g_autofree char *url = NULL;
-      if (!jsc_value_is_string (indexed_value))
-        break;
-      url = jsc_value_to_string (indexed_value);
+  if (JSON_NODE_HOLDS_ARRAY (node)) {
+    JsonArray *array = json_node_get_array (node);
+    urls = g_ptr_array_sized_new (json_array_get_length (array));
+
+    for (guint i = 0; i < json_array_get_length (array); i++) {
+      const char *url = ephy_json_array_get_string (array, i);
+
+      if (!url)
+        continue;
+
       if (ephy_web_extension_api_tabs_url_is_unprivileged (url))
-        g_ptr_array_add (urls, g_steal_pointer (&url));
+        g_ptr_array_add (urls, (char *)url);
     }
 
     return urls;
   }
 
   g_debug ("Received invalid urls property");
-  return urls;
+  return NULL;
 }
 
 static void
 windows_handler_create (EphyWebExtensionSender *sender,
-                        char                   *name,
-                        JSCValue               *args,
+                        const char             *method_name,
+                        JsonArray              *args,
                         GTask                  *task)
 {
-  g_autoptr (JSCValue) create_data_value = jsc_value_object_get_property_at_index (args, 0);
+  JsonObject *create_data = ephy_json_array_get_object (args, 0);
   g_autoptr (GPtrArray) urls = NULL;
   g_autoptr (JsonBuilder) builder = json_builder_new ();
   g_autoptr (JsonNode) root = NULL;
   EphyWindow *window;
 
-  if (jsc_value_is_object (create_data_value)) {
-    urls = get_url_property (create_data_value);
-  }
+  if (create_data)
+    urls = get_url_property (create_data);
 
   window = ephy_window_new ();
 
@@ -312,19 +309,19 @@ windows_handler_create (EphyWebExtensionSender *sender,
 
 static void
 windows_handler_remove (EphyWebExtensionSender *sender,
-                        char                   *name,
-                        JSCValue               *args,
+                        const char             *method_name,
+                        JsonArray              *args,
                         GTask                  *task)
 {
-  g_autoptr (JSCValue) window_id_value = jsc_value_object_get_property_at_index (args, 0);
+  gint64 window_id = ephy_json_array_get_int (args, 0);
   EphyWindow *window;
 
-  if (!jsc_value_is_number (window_id_value)) {
+  if (window_id == -1) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"window.remove(): First argument is not a windowId");
     return;
   }
 
-  window = ephy_web_extension_api_windows_get_window_for_id (jsc_value_to_int32 (window_id_value));
+  window = ephy_web_extension_api_windows_get_window_for_id (window_id);
 
   if (!window) {
     g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_INVALID_ARGUMENT, 
"window.remove(): Failed to find window by id");
@@ -336,7 +333,7 @@ windows_handler_remove (EphyWebExtensionSender *sender,
   g_task_return_pointer (task, NULL, NULL);
 }
 
-static EphyWebExtensionAsyncApiHandler windows_handlers[] = {
+static EphyWebExtensionApiHandler windows_handlers[] = {
   {"get", windows_handler_get},
   {"getCurrent", windows_handler_get_current},
   {"getLastFocused", windows_handler_get_last_focused},
@@ -347,18 +344,18 @@ static EphyWebExtensionAsyncApiHandler windows_handlers[] = {
 
 void
 ephy_web_extension_api_windows_handler (EphyWebExtensionSender *sender,
-                                        char                   *name,
-                                        JSCValue               *args,
+                                        const char             *method_name,
+                                        JsonArray              *args,
                                         GTask                  *task)
 {
   for (guint idx = 0; idx < G_N_ELEMENTS (windows_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = windows_handlers[idx];
+    EphyWebExtensionApiHandler handler = windows_handlers[idx];
 
-    if (g_strcmp0 (handler.name, name) == 0) {
-      handler.execute (sender, name, args, task);
+    if (g_strcmp0 (handler.name, method_name) == 0) {
+      handler.execute (sender, method_name, args, task);
       return;
     }
   }
 
-  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "windows.%s(): 
Not Implemented", name);
+  g_task_return_new_error (task, WEB_EXTENSION_ERROR, WEB_EXTENSION_ERROR_NOT_IMPLEMENTED, "windows.%s(): 
Not Implemented", method_name);
 }
diff --git a/src/webextension/api/windows.h b/src/webextension/api/windows.h
index c5f09a42a..20da2784a 100644
--- a/src/webextension/api/windows.h
+++ b/src/webextension/api/windows.h
@@ -27,8 +27,8 @@
 G_BEGIN_DECLS
 
 void         ephy_web_extension_api_windows_handler                    (EphyWebExtensionSender *sender,
-                                                                        char                   *name,
-                                                                        JSCValue               *value,
+                                                                        const char             *method_name,
+                                                                        JsonArray              *args,
                                                                         GTask                  *task);
 
 char        *ephy_web_extension_api_windows_create_window_json          (EphyWebExtension *self,
diff --git a/src/webextension/ephy-json-utils.c b/src/webextension/ephy-json-utils.c
index 1f1cbccb8..737f81538 100644
--- a/src/webextension/ephy-json-utils.c
+++ b/src/webextension/ephy-json-utils.c
@@ -44,6 +44,22 @@ ephy_json_object_get_string (JsonObject *object,
   return json_node_get_string (node);
 }
 
+/**
+ * ephy_json_object_dup_string:
+ * @object: A valid #JsonObject
+ * @name: The member name
+ *
+ * This safely looks up a string member of a #JsonObject.
+ *
+ * Returns: (transfer full): An allocated string or %NULL if member was missing or not a string.
+ */
+char *
+ephy_json_object_dup_string (JsonObject *object,
+                             const char *name)
+{
+  return g_strdup (ephy_json_object_get_string (object, name));
+}
+
 /**
  * ephy_json_object_get_int:
  * @object: A valid #JsonObject
@@ -62,7 +78,7 @@ ephy_json_object_get_int (JsonObject *object,
   if (!node || !JSON_NODE_HOLDS_VALUE (node))
     return -1;
 
-  if (json_node_get_value_type (node) != G_TYPE_INT64)
+  if (json_node_get_value_type (node) == G_TYPE_STRING)
     return -1;
 
   return json_node_get_int (node);
@@ -72,24 +88,42 @@ ephy_json_object_get_int (JsonObject *object,
  * ephy_json_object_get_double:
  * @object: A valid #JsonObject
  * @name: The member name
+ * @default: Default value
  *
  * This safely looks up a double member of a #JsonObject.
  *
- * Returns: A number or `-1.0` if member was missing or not a number.
+ * Returns: A number or @default if member was missing or not a number.
  */
 double
-ephy_json_object_get_double (JsonObject *object,
-                             const char *name)
+ephy_json_object_get_double_with_default (JsonObject *object,
+                                          const char *name,
+                                          double      default_value)
 {
   JsonNode *node = json_object_get_member (object, name);
 
   if (!node || !JSON_NODE_HOLDS_VALUE (node))
-    return -1;
+    return default_value;
 
-  if (json_node_get_value_type (node) != G_TYPE_DOUBLE)
-    return -1;
+  if (json_node_get_value_type (node) == G_TYPE_STRING)
+    return default_value;
 
-  return json_node_get_int (node);
+  return json_node_get_double (node);
+}
+
+/**
+ * ephy_json_object_get_double:
+ * @object: A valid #JsonObject
+ * @name: The member name
+ *
+ * This safely looks up a double member of a #JsonObject.
+ *
+ * Returns: A number or `-1.0` if member was missing or not a number.
+ */
+double
+ephy_json_object_get_double (JsonObject *object,
+                             const char *name)
+{
+  return ephy_json_object_get_double_with_default (object, name, -1.0);
 }
 
 /**
@@ -142,7 +176,7 @@ ephy_json_object_get_object (JsonObject *object,
  *
  * This safely looks up a boolean member of a #JsonObject.
  *
- * Returns: A number or @default if member was missing or not a boolean.
+ * Returns: A number or @default_value if member was missing or not a boolean.
  */
 gboolean
 ephy_json_object_get_boolean (JsonObject *object,
@@ -154,7 +188,7 @@ ephy_json_object_get_boolean (JsonObject *object,
   if (!node || !JSON_NODE_HOLDS_VALUE (node))
     return default_value;
 
-  if (json_node_get_value_type (node) != G_TYPE_BOOLEAN)
+  if (json_node_get_value_type (node) == G_TYPE_STRING)
     return default_value;
 
   return json_node_get_boolean (node);
@@ -197,6 +231,85 @@ ephy_json_node_to_string (JsonNode *node)
   return json_node_get_string (node);
 }
 
+/**
+ * ephy_json_node_get_int:
+ * @node: (nullable): A #JsonNode or %NULL
+ *
+ * This safely turns a #JsonNode into an int.
+ *
+ * Returns: (transfer none): A int or -1 if not a number.
+ */
+gint64
+ephy_json_node_get_int (JsonNode *node)
+{
+  if (!node || !JSON_NODE_HOLDS_VALUE (node))
+    return -1;
+
+  if (json_node_get_value_type (node) == G_TYPE_STRING)
+    return -1;
+
+  return json_node_get_int (node);
+}
+
+/**
+ * ephy_json_node_get_double:
+ * @node: (nullable): A #JsonNode or %NULL
+ *
+ * This safely turns a #JsonNode into a double.
+ *
+ * Returns: (transfer none): A double or -1.0 if not a number.
+ */
+double
+ephy_json_node_get_double (JsonNode *node)
+{
+  if (!node || !JSON_NODE_HOLDS_VALUE (node))
+    return -1.0;
+
+  if (json_node_get_value_type (node) == G_TYPE_STRING)
+    return -1.0;
+
+  return json_node_get_double (node);
+}
+
+/**
+ * ephy_json_array_get_string_with_default:
+ * @array: A #JsonArray
+ * @default_value: Default string value
+ *
+ * This safely gets a string from an array.
+ *
+ * Returns: (transfer none): A string or @default_value if not a string.
+ */
+const char *
+ephy_json_array_get_string_with_default (JsonArray  *array,
+                                         guint       index,
+                                         const char *default_value)
+{
+  const char *value = ephy_json_node_to_string (json_array_get_element (array, index));
+  if (!value)
+    return default_value;
+
+  return value;
+}
+
+/**
+ * ephy_json_array_get_element:
+ * @array: A #JsonArray
+ *
+ * This safely returns an element from an array.
+ *
+ * Returns: (transfer none): A #JsonNode or %NULL if out of bounds.
+ */
+JsonNode *
+ephy_json_array_get_element (JsonArray *array,
+                             guint      index)
+{
+  if (index >= json_array_get_length (array))
+    return NULL;
+
+  return json_array_get_element (array, index);
+}
+
 /**
  * ephy_json_array_get_string:
  * @array: A #JsonArray
@@ -209,7 +322,7 @@ const char *
 ephy_json_array_get_string (JsonArray *array,
                             guint      index)
 {
-  return ephy_json_node_to_string (json_array_get_element (array, index));
+  return ephy_json_node_to_string (ephy_json_array_get_element (array, index));
 }
 
 /**
@@ -224,5 +337,67 @@ JsonObject *
 ephy_json_array_get_object (JsonArray *array,
                             guint      index)
 {
-  return ephy_json_node_get_object (json_array_get_element (array, index));
+  return ephy_json_node_get_object (ephy_json_array_get_element (array, index));
+}
+
+/**
+ * ephy_json_array_get_int:
+ * @array: A #JsonArray
+ *
+ * This safely gets an int from an array.
+ *
+ * Returns: (transfer none): An int or -1 if not a number.
+ */
+gint64
+ephy_json_array_get_int (JsonArray *array,
+                         guint      index)
+{
+  return ephy_json_node_get_int (ephy_json_array_get_element (array, index));
+}
+
+/**
+ * ephy_json_array_get_double:
+ * @array: A #JsonArray
+ *
+ * This safely gets a double from an array.
+ *
+ * Returns: (transfer none): An double or -1.0 if not a number.
+ */
+double
+ephy_json_array_get_double (JsonArray *array,
+                            guint      index)
+{
+  return ephy_json_node_get_double (ephy_json_array_get_element (array, index));
+}
+
+/**
+ * ephy_json_object_get_string_array:
+ * @object: A #JsonObject
+ * @name: Property name
+ *
+ * This safely gets a string array from an object.
+ *
+ * Returns: (transfer full): A valid, maybe empty, #GPtrArray of strings.
+ */
+GPtrArray *
+ephy_json_object_get_string_array (JsonObject *object,
+                                   const char *name)
+{
+  JsonArray *array = ephy_json_object_get_array (object, name);
+  GPtrArray *strings;
+
+  if (!array)
+    return g_ptr_array_new ();
+
+  strings = g_ptr_array_new_full (json_array_get_length (array), g_free);
+  for (guint i = 0; i < json_array_get_length (array); i++) {
+    const char *value = ephy_json_array_get_string (array, i);
+
+    if (!value)
+      break;
+
+    g_ptr_array_add (strings, g_strdup (value));
+  }
+
+  return strings;
 }
diff --git a/src/webextension/ephy-json-utils.h b/src/webextension/ephy-json-utils.h
index 04c9f42f5..c5e5fe434 100644
--- a/src/webextension/ephy-json-utils.h
+++ b/src/webextension/ephy-json-utils.h
@@ -25,6 +25,9 @@
 const char    *ephy_json_object_get_string           (JsonObject          *object,
                                                       const char          *name);
 
+char          *ephy_json_object_dup_string           (JsonObject          *object,
+                                                      const char          *name);
+
 gboolean      ephy_json_object_get_boolean          (JsonObject          *object,
                                                      const char          *name,
                                                      gboolean             default_value);
@@ -32,6 +35,11 @@ gboolean      ephy_json_object_get_boolean          (JsonObject          *object
 double        ephy_json_object_get_double           (JsonObject          *object,
                                                      const char          *name);
 
+double        ephy_json_object_get_double_with_default
+                                                    (JsonObject          *object,
+                                                     const char          *name,
+                                                     double               default_value);
+
 gint64        ephy_json_object_get_int              (JsonObject          *object,
                                                      const char          *name);
 
@@ -41,12 +49,33 @@ JsonArray    *ephy_json_object_get_array            (JsonObject          *object
 JsonObject   *ephy_json_object_get_object           (JsonObject          *object,
                                                      const char          *name);
 
+GPtrArray    *ephy_json_object_get_string_array     (JsonObject          *object,
+                                                     const char          *name);
+
 JsonObject   *ephy_json_node_get_object             (JsonNode            *node);
 
 const char   *ephy_json_node_to_string              (JsonNode            *node);
 
+gint64       ephy_json_node_get_int                 (JsonNode            *node);
+
+double       ephy_json_node_get_double              (JsonNode            *node);
+
 const char   *ephy_json_array_get_string            (JsonArray           *array,
                                                      guint                index);
 
+const char   *ephy_json_array_get_string_with_default
+                                                    (JsonArray           *array,
+                                                     guint                index,
+                                                     const char          *default_value);
+
 JsonObject   *ephy_json_array_get_object            (JsonArray           *array,
                                                      guint                index);
+
+gint64       ephy_json_array_get_int                (JsonArray           *array,
+                                                     guint                index);
+
+double       ephy_json_array_get_double             (JsonArray           *array,
+                                                     guint                index);
+
+JsonNode    *ephy_json_array_get_element            (JsonArray           *array,
+                                                     guint                index);
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 9de0e55b0..4ca8033d2 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -49,7 +49,7 @@
 #include <json-glib/json-glib.h>
 
 static void handle_message_reply (EphyWebExtension *web_extension,
-                                  JSCValue         *args);
+                                  JsonArray        *args);
 
 struct _EphyWebExtensionManager {
   GObject parent_instance;
@@ -69,7 +69,7 @@ struct _EphyWebExtensionManager {
 
 G_DEFINE_TYPE (EphyWebExtensionManager, ephy_web_extension_manager, G_TYPE_OBJECT)
 
-EphyWebExtensionAsyncApiHandler api_handlers[] = {
+EphyWebExtensionApiHandler api_handlers[] = {
   {"alarms", ephy_web_extension_api_alarms_handler},
   {"cookies", ephy_web_extension_api_cookies_handler},
   {"downloads", ephy_web_extension_api_downloads_handler},
@@ -573,14 +573,14 @@ respond_with_error (WebKitUserMessage *message,
 typedef struct {
   EphyWebExtensionSender *sender;
   WebKitUserMessage *message;
-  JSCValue *args;
+  JsonNode *args;
 } ApiHandlerData;
 
 static void
 api_handler_data_free (ApiHandlerData *data)
 {
   g_object_unref (data->message);
-  g_object_unref (data->args);
+  json_node_unref (data->args);
   g_free (data->sender);
   g_free (data);
 }
@@ -590,11 +590,11 @@ api_handler_data_new (EphyWebExtension  *extension,
                       WebKitWebView     *view,
                       guint64            frame_id,
                       WebKitUserMessage *message,
-                      JSCValue          *args)
+                      JsonNode          *args)
 {
   ApiHandlerData *data = g_new (ApiHandlerData, 1);
   data->message = g_object_ref (message);
-  data->args = g_object_ref (args);
+  data->args = json_node_ref (args);
   data->sender = g_new (EphyWebExtensionSender, 1);
   data->sender->extension = extension;
   data->sender->view = view;
@@ -629,25 +629,33 @@ extension_view_handle_user_message (WebKitWebView     *web_view,
                                     gpointer           user_data)
 {
   EphyWebExtension *web_extension = user_data;
-  g_autoptr (JSCContext) js_context = NULL;
-  g_autoptr (JSCValue) args = NULL;
   const char *name = webkit_user_message_get_name (message);
+  g_autoptr (GError) error = NULL;
   g_auto (GStrv) split = NULL;
   const char *guid;
-  const char *json_args;
+  const char *json_string;
+  g_autoptr (JsonNode) json = NULL;
+  JsonArray *json_args;
   guint64 frame_id;
 
-  g_variant_get (webkit_user_message_get_parameters (message), "(&st&s)", &guid, &frame_id, &json_args);
+  g_variant_get (webkit_user_message_get_parameters (message), "(&st&s)", &guid, &frame_id, &json_string);
+
+  LOG ("%s(): Called for %s, function %s (%s)\n", __FUNCTION__, ephy_web_extension_get_name (web_extension), 
name, json_string);
 
-  js_context = jsc_context_new ();
-  args = jsc_value_new_from_json (js_context, json_args);
+  json = json_from_string (json_string, &error);
+  if (!json || !JSON_NODE_HOLDS_ARRAY (json)) {
+    g_warning ("Received invalid JSON: %s", error ? error->message : "JSON was not an array");
+    respond_with_error (message, "Invalid function arguments");
+    return TRUE;
+  }
 
-  LOG ("%s(): Called for %s, function %s (%s)\n", __FUNCTION__, ephy_web_extension_get_name (web_extension), 
name, json_args);
+  json_args = json_node_get_array (json);
+  json_array_seal (json_args);
 
   /* Private API for message replies handled by the manager. */
   if (strcmp (name, "runtime._sendMessageReply") == 0) {
     WebKitUserMessage *reply = webkit_user_message_new ("", g_variant_new_string (""));
-    handle_message_reply (web_extension, args);
+    handle_message_reply (web_extension, json_args);
     webkit_user_message_send_reply (message, reply);
     return TRUE;
   }
@@ -659,15 +667,15 @@ extension_view_handle_user_message (WebKitWebView     *web_view,
   }
 
   for (guint idx = 0; idx < G_N_ELEMENTS (api_handlers); idx++) {
-    EphyWebExtensionAsyncApiHandler handler = api_handlers[idx];
+    EphyWebExtensionApiHandler handler = api_handlers[idx];
 
     if (g_strcmp0 (handler.name, split[0]) == 0) {
       /* TODO: Cancellable */
       GTask *task = g_task_new (web_extension, NULL, 
(GAsyncReadyCallback)on_web_extension_api_handler_finish, NULL);
-      ApiHandlerData *data = api_handler_data_new (web_extension, web_view, frame_id, message, args);
+      ApiHandlerData *data = api_handler_data_new (web_extension, web_view, frame_id, message, json);
       g_task_set_task_data (task, data, (GDestroyNotify)api_handler_data_free);
 
-      handler.execute (data->sender, split[1], args, task);
+      handler.execute (data->sender, split[1], json_args, task);
       return TRUE;
     }
   }
@@ -683,30 +691,38 @@ content_scripts_handle_user_message (WebKitWebView     *web_view,
                                      gpointer           user_data)
 {
   EphyWebExtension *web_extension = user_data;
-  g_autoptr (JSCContext) js_context = NULL;
-  g_autoptr (JSCValue) args = NULL;
+  g_autoptr (GError) error = NULL;
   const char *name = webkit_user_message_get_name (message);
   g_auto (GStrv) split = NULL;
-  const char *json_args;
+  const char *json_string;
+  g_autoptr (JsonNode) json = NULL;
+  JsonArray *json_args;
   const char *extension_guid;
   GTask *task;
   guint64 frame_id;
 
-  g_variant_get (webkit_user_message_get_parameters (message), "(&st&s)", &extension_guid, &frame_id, 
&json_args);
+  g_variant_get (webkit_user_message_get_parameters (message), "(&st&s)", &extension_guid, &frame_id, 
&json_string);
 
   /* Multiple extensions can send user-messages from the same web-view, so only the target one handles this. 
*/
   if (strcmp (extension_guid, ephy_web_extension_get_guid (web_extension)) != 0)
     return FALSE;
 
-  js_context = jsc_context_new ();
-  args = jsc_value_new_from_json (js_context, json_args);
+  LOG ("%s(): Called for %s, function %s (%s)\n", __FUNCTION__, ephy_web_extension_get_name (web_extension), 
name, json_string);
 
-  LOG ("%s(): Called for %s, function %s (%s)\n", __FUNCTION__, ephy_web_extension_get_name (web_extension), 
name, json_args);
+  json = json_from_string (json_string, &error);
+  if (!json || !JSON_NODE_HOLDS_ARRAY (json)) {
+    g_warning ("Received invalid JSON: %s", error ? error->message : "JSON was not an array");
+    respond_with_error (message, "Invalid function arguments");
+    return TRUE;
+  }
+
+  json_args = json_node_get_array (json);
+  json_array_seal (json_args);
 
   /* Private API for message replies handled by the manager. */
   if (strcmp (name, "runtime._sendMessageReply") == 0) {
     WebKitUserMessage *reply = webkit_user_message_new ("", g_variant_new_string (""));
-    handle_message_reply (web_extension, args);
+    handle_message_reply (web_extension, json_args);
     webkit_user_message_send_reply (message, reply);
     return TRUE;
   }
@@ -719,21 +735,21 @@ content_scripts_handle_user_message (WebKitWebView     *web_view,
 
   /* Content Scripts are very limited in their API access compared to extension views so we handle them 
individually. */
   if (strcmp (split[0], "storage") == 0) {
-    ApiHandlerData *data = api_handler_data_new (web_extension, web_view, frame_id, message, args);
+    ApiHandlerData *data = api_handler_data_new (web_extension, web_view, frame_id, message, json);
     task = g_task_new (web_extension, NULL, (GAsyncReadyCallback)on_web_extension_api_handler_finish, NULL);
     g_task_set_task_data (task, data, (GDestroyNotify)api_handler_data_free);
 
-    ephy_web_extension_api_storage_handler (data->sender, split[1], args, task);
+    ephy_web_extension_api_storage_handler (data->sender, split[1], json_args, task);
     return TRUE;
   }
 
   if (strcmp (name, "runtime.sendMessage") == 0) {
-    ApiHandlerData *data = api_handler_data_new (web_extension, web_view, frame_id, message, args);
+    ApiHandlerData *data = api_handler_data_new (web_extension, web_view, frame_id, message, json);
     task = g_task_new (web_extension, NULL, (GAsyncReadyCallback)on_web_extension_api_handler_finish, NULL);
 
     g_task_set_task_data (task, data, (GDestroyNotify)api_handler_data_free);
 
-    ephy_web_extension_api_runtime_handler (data->sender, split[1], args, task);
+    ephy_web_extension_api_runtime_handler (data->sender, split[1], json_args, task);
     return TRUE;
   }
 
@@ -1433,31 +1449,29 @@ ephy_web_extension_manager_handle_context_menu_action (EphyWebExtensionManager *
 
 static void
 handle_message_reply (EphyWebExtension *web_extension,
-                      JSCValue         *args)
+                      JsonArray        *args)
 {
   EphyWebExtensionManager *manager = ephy_web_extension_manager_get_default ();
   GHashTable *pending_messages = g_hash_table_lookup (manager->pending_messages, web_extension);
   GTask *pending_task;
-  g_autofree char *message_guid = NULL;
-  g_autoptr (JSCValue) message_guid_value = NULL;
-  g_autoptr (JSCValue) reply_value = NULL;
+  const char *message_guid;
+  JsonNode *reply;
 
-  message_guid_value = jsc_value_object_get_property_at_index (args, 0);
-  if (!jsc_value_is_string (message_guid_value)) {
+  message_guid = ephy_json_array_get_string (args, 0);
+  if (!message_guid) {
     g_debug ("Received invalid message reply");
     return;
   }
 
-  message_guid = jsc_value_to_string (message_guid_value);
   pending_task = g_hash_table_lookup (pending_messages, message_guid);
   if (!pending_task) {
     g_debug ("Received message not found in pending replies");
     return;
   }
-
-  reply_value = jsc_value_object_get_property_at_index (args, 1);
   g_hash_table_steal (pending_messages, message_guid);
-  g_task_return_pointer (pending_task, jsc_value_to_json (reply_value, 0), g_free);
+
+  reply = ephy_json_array_get_element (args, 1);
+  g_task_return_pointer (pending_task, reply ? json_to_string (reply, FALSE) : NULL, g_free);
 }
 
 typedef struct {
diff --git a/src/webextension/ephy-web-extension.h b/src/webextension/ephy-web-extension.h
index c9f470a91..8fbd140bc 100644
--- a/src/webextension/ephy-web-extension.h
+++ b/src/webextension/ephy-web-extension.h
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "ephy-debug.h"
+#include "ephy-json-utils.h"
 #include "ephy-window.h"
 
 #include <gdk-pixbuf/gdk-pixbuf.h>
@@ -47,18 +48,8 @@ typedef struct {
   guint64 frame_id;
 } EphyWebExtensionSender;
 
-typedef void (*executeTaskHandler)(EphyWebExtensionSender *sender,
-                                   char                   *name,
-                                   JSCValue               *args,
-                                   GTask                  *task);
-
-typedef char *(*executeHandler)(EphyWebExtensionSender  *sender,
-                                char                    *name,
-                                JSCValue                *args,
-                                GError                 **error);
-
 typedef void (*EphyApiExecuteFunc)(EphyWebExtensionSender *sender,
-                                   char                   *name,
+                                   const char             *method_name,
                                    JsonArray              *args,
                                    GTask                  *task);
 
@@ -80,11 +71,6 @@ typedef struct {
   EphyApiExecuteFunc execute;
 } EphyWebExtensionApiHandler;
 
-typedef struct {
-  char *name;
-  executeTaskHandler execute;
-} EphyWebExtensionAsyncApiHandler;
-
 GdkPixbuf             *ephy_web_extension_get_icon                        (EphyWebExtension *self,
                                                                            gint64            size);
 
diff --git a/src/webextension/meson.build b/src/webextension/meson.build
index 006ea40c3..3d7fda2df 100644
--- a/src/webextension/meson.build
+++ b/src/webextension/meson.build
@@ -1,6 +1,5 @@
 ephywebextension_src = [
   'webextension/api/alarms.c',
-  'webextension/api/api-utils.c',
   'webextension/api/cookies.c',
   'webextension/api/downloads.c',
   'webextension/api/menus.c',


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