]> spindle.queued.net Git - midori/commitdiff
Introduce Midori.SpeedDial and unit test
authorChristian Dywan <christian@twotoasts.de>
Fri, 31 Aug 2012 21:47:03 +0000 (23:47 +0200)
committerChristian Dywan <christian@twotoasts.de>
Fri, 31 Aug 2012 21:47:03 +0000 (23:47 +0200)
Fixes: https://bugs.launchpad.net/midori/+bug/1038634
midori/main.c
midori/midori-browser.c
midori/midori-speeddial.vala [new file with mode: 0644]
midori/midori-view.c
midori/sokoke.c
tests/speeddial.vala [new file with mode: 0644]

index 3d93e031e451fa30119dbd1245f433b55506cb7d..e69670a3caa1eaab8254e80d1a9c925f02756677 100644 (file)
@@ -1578,92 +1578,6 @@ signal_handler (int signal_id)
 }
 #endif
 
-static GKeyFile*
-speeddial_new_from_file (const gchar* config,
-                         GError**     error)
-{
-
-    GKeyFile* key_file = g_key_file_new ();
-    gchar* config_file = g_build_filename (config, "speeddial", NULL);
-    guint i = 0;
-    gchar* json_content;
-    gsize json_length;
-    GString* script;
-    JSGlobalContextRef js_context;
-    gchar* keyfile;
-    gchar* thumb_dir;
-    gchar** tiles;
-
-    if (g_key_file_load_from_file (key_file, config_file, G_KEY_FILE_NONE, error))
-    {
-        g_free (config_file);
-        return key_file;
-    }
-
-    katze_assign (config_file, g_build_filename (config, "speeddial.json", NULL));
-    if (!g_file_get_contents (config_file, &json_content, &json_length, NULL))
-    {
-        katze_assign (json_content, g_strdup ("'{}'"));
-        json_length = strlen ("'{}'");
-    }
-
-    script = g_string_sized_new (json_length);
-    g_string_append (script, "var json = JSON.parse (");
-    g_string_append_len (script, json_content, json_length);
-    g_string_append (script, "); "
-        "var keyfile = '';"
-        "for (var i in json['shortcuts']) {"
-        "var tile = json['shortcuts'][i];"
-        "keyfile += '[Dial ' + tile['id'].substring (1) + ']\\n'"
-        "        +  'uri=' + tile['href'] + '\\n'"
-        "        +  'img=' + tile['img'] + '\\n'"
-        "        +  'title=' + tile['title'] + '\\n\\n';"
-        "} "
-        "var columns = json['width'] ? json['width'] : 3;"
-        "var rows = json['shortcuts'] ? json['shortcuts'].length / columns : 0;"
-        "keyfile += '[settings]\\n'"
-        "        +  'columns=' + columns + '\\n'"
-        "        +  'rows=' + (rows > 3 ? rows : 3) + '\\n\\n';"
-        "keyfile;");
-    g_free (json_content);
-    js_context = JSGlobalContextCreateInGroup (NULL, NULL);
-    keyfile = sokoke_js_script_eval (js_context, script->str, NULL);
-    JSGlobalContextRelease (js_context);
-    g_string_free (script, TRUE);
-    g_key_file_load_from_data (key_file, keyfile, -1, 0, NULL);
-    g_free (keyfile);
-    tiles = g_key_file_get_groups (key_file, NULL);
-    thumb_dir = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_cache_dir (), "thumbnails", NULL);
-    if (!g_file_test (thumb_dir, G_FILE_TEST_EXISTS))
-        katze_mkdir_with_parents (thumb_dir, 0700);
-    g_free (thumb_dir);
-
-    while (tiles[i] != NULL)
-    {
-        gsize sz;
-        gchar* uri = g_key_file_get_string (key_file, tiles[i], "uri", NULL);
-        gchar* img = g_key_file_get_string (key_file, tiles[i], "img", NULL);
-        if (img != NULL && (uri && *uri && *uri != '#'))
-        {
-            guchar* decoded = g_base64_decode (img, &sz);
-            gchar* thumb_path = sokoke_build_thumbnail_path (uri);
-            g_file_set_contents (thumb_path, (gchar*)decoded, sz, NULL);
-            g_free (thumb_path);
-            g_free (decoded);
-        }
-        g_free (img);
-        g_free (uri);
-        g_key_file_remove_key (key_file, tiles[i], "img", NULL);
-        i++;
-    }
-    g_strfreev (tiles);
-
-    katze_assign (config_file, g_build_filename (config, "speeddial", NULL));
-    sokoke_key_file_save_to_file (key_file, config_file, NULL);
-    g_free (config_file);
-    return key_file;
-}
-
 static void
 midori_soup_session_block_uris_cb (SoupSession* session,
                                    SoupMessage* msg,
@@ -1970,7 +1884,7 @@ main (int    argc,
     gchar** extensions;
     MidoriWebSettings* settings;
     gchar* config_file;
-    GKeyFile* speeddial;
+    MidoriSpeedDial* dial;
     MidoriStartup load_on_startup;
     KatzeArray* search_engines;
     KatzeArray* bookmarks;
@@ -2476,8 +2390,8 @@ main (int    argc,
     }
     midori_startup_timer ("History read: \t%f");
 
-    error = NULL;
-    speeddial = speeddial_new_from_file (config, &error);
+    katze_assign (config_file, g_build_filename (config, "speeddial", NULL));
+    dial = midori_speed_dial_new (config_file, NULL);
 
     /* In case of errors */
     if (error_messages->len)
@@ -2594,7 +2508,7 @@ main (int    argc,
                        "trash", trash,
                        "search-engines", search_engines,
                        "history", history,
-                       "speed-dial", speeddial,
+                       "speed-dial", dial->keyfile,
                        NULL);
     g_object_unref (history);
     g_object_unref (search_engines);
@@ -2657,7 +2571,7 @@ main (int    argc,
     }
 
     g_object_unref (settings);
-    g_key_file_free (speeddial);
+    g_object_unref (dial);
     g_object_unref (app);
     g_free (config_file);
     return 0;
index ddd523fe8fee847a32f583f5c2ac2c4c0bce3709..7127d241adafda3c6e0525c6253c4e6e30f21afa 100644 (file)
@@ -1234,75 +1234,27 @@ midori_view_save_as_cb (GtkWidget*   menuitem,
     midori_browser_save_uri (browser, MIDORI_VIEW (view), uri);
 }
 
-static gchar*
-midori_browser_speed_dial_get_next_free_slot (MidoriView* view)
-{
-    MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (view));
-    GKeyFile* key_file;
-    guint slot_count = 0, slot = 1, i;
-    gchar** groups;
-
-    g_object_get (browser, "speed-dial", &key_file, NULL);
-
-    groups = g_key_file_get_groups (key_file, NULL);
-    for (i = 0; groups[i]; i++)
-    {
-        if (g_key_file_has_key (key_file, groups[i], "uri", NULL))
-            slot_count++;
-    }
-
-    while (slot <= slot_count)
-    {
-        gchar* dial_id = g_strdup_printf ("Dial %d", slot);
-        if (!g_key_file_has_group (key_file, dial_id))
-        {
-            g_free (dial_id);
-            return g_strdup_printf ("s%d", slot);
-        }
-        g_free (dial_id);
-        slot++;
-    }
-    return g_strdup_printf ("s%d", slot_count + 1);
-}
-
 static void
 midori_browser_add_speed_dial (MidoriBrowser* browser)
 {
     GdkPixbuf* img;
     GtkWidget* view = midori_browser_get_current_tab (browser);
-    gchar* uri = g_strdup (midori_view_get_display_uri (MIDORI_VIEW (view)));
-    gchar* title = g_strdup (midori_view_get_display_title (MIDORI_VIEW (view)));
-    gchar* slot_id = midori_browser_speed_dial_get_next_free_slot (MIDORI_VIEW (view));
+    gchar* slot_id = midori_speed_dial_get_next_free_slot_fk (browser->speeddial);
+    gchar* uri;
+    gchar* title;
 
     if (slot_id == NULL)
-    {
-        g_free (uri);
-        g_free (title);
         return;
-    }
 
+    uri = g_strdup (midori_view_get_display_uri (MIDORI_VIEW (view)));
+    title = g_strdup (midori_view_get_display_title (MIDORI_VIEW (view)));
     if ((img = midori_view_get_snapshot (MIDORI_VIEW (view), 240, 160)))
     {
-        GKeyFile* key_file;
         gchar* dial_id = g_strdup_printf ("Dial %s", slot_id + 1);
-        gchar* file_path = sokoke_build_thumbnail_path (uri);
-        gchar* thumb_dir = g_build_path (G_DIR_SEPARATOR_S, midori_paths_get_cache_dir (), "thumbnails", NULL);
-        g_object_get (browser, "speed-dial", &key_file, NULL);
-
-        g_key_file_set_string (key_file, dial_id, "uri", uri);
-        g_key_file_set_string (key_file, dial_id, "title", title);
-
-        if (!g_file_test (thumb_dir, G_FILE_TEST_EXISTS))
-            katze_mkdir_with_parents (thumb_dir, 0700);
-
-        gdk_pixbuf_save (img, file_path, "png", NULL, "compression", "7", NULL);
-
-        midori_view_save_speed_dial_config (MIDORI_VIEW (view), key_file);
-
-        g_object_unref (img);
-        g_free (file_path);
-        g_free (thumb_dir);
+        midori_speed_dial_add_fk (dial_id, uri, title, img, browser->speeddial);
         g_free (dial_id);
+        midori_view_save_speed_dial_config (MIDORI_VIEW (view), browser->speeddial);
+        g_object_unref (img);
     }
     g_free (uri);
     g_free (title);
diff --git a/midori/midori-speeddial.vala b/midori/midori-speeddial.vala
new file mode 100644 (file)
index 0000000..1538981
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ Copyright (C) 2011-2012 Christian Dywan <christian@twotoats.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ See the file COPYING for the full license text.
+*/
+
+namespace Katze {
+    extern static string mkdir_with_parents (string pathname, int mode);
+}
+
+namespace Sokoke {
+    extern static string js_script_eval (void* ctx, string script, void* error);
+    extern static string build_thumbnail_path (string uri);
+}
+
+namespace Midori {
+    public class SpeedDial : GLib.Object {
+        public GLib.KeyFile keyfile;
+
+        public SpeedDial (string filename, string? fallback = null) {
+            keyfile = new GLib.KeyFile ();
+            try {
+                keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE);
+            }
+            catch (GLib.Error io_error) {
+                string json;
+                size_t len;
+                try {
+                    FileUtils.get_contents (fallback ?? (filename + ".json"),
+                                            out json, out len);
+                }
+                catch (GLib.Error fallback_error) {
+                    json = "'{}'";
+                    len = 4;
+                }
+
+                var script = new StringBuilder.sized (len);
+                script.append ("var json = JSON.parse (");
+                script.append_len (json, (ssize_t)len);
+                script.append ("""
+        );
+        var keyfile = '';
+        for (var i in json['shortcuts']) {
+        var tile = json['shortcuts'][i];
+        keyfile += '[Dial ' + tile['id'].substring (1) + ']\n'
+                +  'uri=' + tile['href'] + '\n'
+                +  'img=' + tile['img'] + '\n'
+                +  'title=' + tile['title'] + '\n\n';
+        }
+        var columns = json['width'] ? json['width'] : 3;
+        var rows = json['shortcuts'] ? json['shortcuts'].length / columns : 0;
+        keyfile += '[settings]\n'
+                +  'columns=' + columns + '\n'
+                +  'rows=' + (rows > 3 ? rows : 3) + '\n\n';
+        keyfile;
+                    """);
+
+                try {
+                    keyfile.load_from_data (
+                        Sokoke.js_script_eval (null, script.str, null),
+                        -1, 0);
+                }
+                catch (GLib.Error eval_error) {
+                    GLib.critical ("Failed to parse %s as speed dial JSON: %s",
+                                   fallback ?? (filename + ".json"), eval_error.message);
+                }
+                Katze.mkdir_with_parents (
+                    Path.build_path (Path.DIR_SEPARATOR_S,
+                                     Environment.get_user_cache_dir (),
+                                     "midori", "thumbnails"), 0700);
+
+                foreach (string tile in keyfile.get_groups ()) {
+                    try {
+                        string img = keyfile.get_string (tile, "img");
+                        string uri = keyfile.get_string (tile, "uri");
+                        if (img != null && uri[0] != '\0' && uri[0] != '#') {
+                            uchar[] decoded = Base64.decode (img);
+                            FileUtils.set_data (Sokoke.build_thumbnail_path (uri), decoded);
+                        }
+                        keyfile.remove_key (tile, "img");
+                    }
+                    catch (GLib.Error img_error) {
+                        /* img and uri can be missing */
+                    }
+                }
+            }
+        }
+
+        public static string get_next_free_slot_fk (KeyFile keyfile) {
+            uint slot_count = 0;
+            foreach (string tile in keyfile.get_groups ()) {
+                try {
+                    if (keyfile.has_key (tile, "uri"))
+                        slot_count++;
+                }
+                catch (KeyFileError error) { }
+            }
+
+            uint slot = 1;
+            while (slot <= slot_count) {
+                string tile = "Dial %u".printf (slot);
+                if (!keyfile.has_group (tile))
+                    return "s%u".printf (slot);
+                slot++;
+            }
+            return "s%u".printf (slot_count + 1);
+        }
+
+        public static void add_fk (string id, string uri, string title, Gdk.Pixbuf img, KeyFile keyfile) {
+            keyfile.set_string (id, "uri", uri);
+            keyfile.set_string (id, "title", title);
+
+            Katze.mkdir_with_parents (Path.build_path (Path.DIR_SEPARATOR_S,
+                Paths.get_cache_dir (), "thumbnails"), 0700);
+            string filename = Sokoke.build_thumbnail_path (uri);
+            try {
+                img.save (filename, "png", null, "compression", "7", null);
+            }
+            catch (Error error) {
+                critical ("Failed to save speed dial thumbnail: %s", error.message);
+            }
+        }
+
+        public static string? get_html_fk (KeyFile? keyfile,
+            bool close_buttons_left, GLib.Object view, bool load_missing) throws Error {
+
+            string? head = null;
+            string filename = Paths.get_res_filename ("speeddial-head.html");
+            if (keyfile != null
+             && FileUtils.get_contents (filename, out head, null)) {
+                string header = head.replace ("{title}", _("Speed Dial")).
+                    replace ("{click_to_add}", _("Click to add a shortcut")).
+                    replace ("{enter_shortcut_address}", _("Enter shortcut address")).
+                    replace ("{enter_shortcut_name}", _("Enter shortcut title")).
+                    replace ("{are_you_sure}", _("Are you sure you want to delete this shortcut?"));
+                var markup = new StringBuilder (header);
+
+                uint slot_count = 1;
+                foreach (string tile in keyfile.get_groups ()) {
+                    try {
+                        if (keyfile.has_key (tile, "uri"))
+                            slot_count++;
+                    }
+                    catch (KeyFileError error) { }
+                }
+
+                /* Try to guess the best X by X grid size */
+                uint grid_index = 3;
+                while ((grid_index * grid_index) < slot_count)
+                    grid_index++;
+
+                /* Percent width size of one slot */
+                uint slot_size = (100 / grid_index);
+
+                /* No editing in private/ app mode or without scripts */
+                markup.append_printf (
+                    "%s<style>.cross { display:none }</style>%s" +
+                    "<style> div.shortcut { height: %d%%; width: %d%%; }</style>\n",
+                    Paths.is_readonly () ? "" : "<noscript>",
+                    Paths.is_readonly () ? "" : "</noscript>",
+                    slot_size + 1, slot_size - 4);
+
+                /* Combined width of slots should always be less than 100%.
+                 * Use half of the remaining percentage as a margin size */
+                uint div_factor;
+                if (slot_size * grid_index >= 100 && grid_index > 4)
+                    div_factor = 8;
+                else
+                    div_factor = 2;
+                uint margin = (100 - ((slot_size - 4) * grid_index)) / div_factor;
+                if (margin > 9)
+                    margin = margin % 10;
+
+                markup.append_printf (
+                    "<style> body { overflow:hidden } #content { margin-left: %u%%; }</style>", margin);
+                if (close_buttons_left)
+                    markup.append_printf (
+                        "<style>.cross { left: -14px }</style>");
+
+                foreach (string tile in keyfile.get_groups ()) {
+                    try {
+                        string uri = keyfile.get_string (tile, "uri");
+                        if (uri != null && uri.str ("://") != null && tile.has_prefix ("Dial ")) {
+                            string title = keyfile.get_string (tile, "title");
+                            string thumb_filename = Sokoke.build_thumbnail_path (uri);
+                            uint slot = tile.substring (5, -1).to_int ();
+                            string encoded;
+                            try {
+                                uint8[] thumb;
+                                FileUtils.get_data (thumb_filename, out thumb);
+                                encoded = Base64.encode (thumb);
+                            }
+                            catch (FileError error) {
+                                encoded = null;
+                                if (load_missing)
+                                    /* FIXME: midori_view_speed_dial_get_thumb (view, tile, uri); */
+                                    critical ("FIXME midori_view_speed_dial_get_thumb");
+                            }
+                            markup.append_printf ("""
+                                <div class="shortcut" id="s%u"><div class="preview">
+                                <a class="cross" href="#" onclick='clearShortcut("s%u");'></a>
+                                <a href="%s"><img src="data:image/png;base64,%s" title='%s'></a>
+                                </div><div class="title" onclick='renameShortcut("s%u");'>%s</div></div>
+                                """,
+                                slot, slot, uri, encoded ?? "", title, slot, title ?? "");
+                        }
+                        else if (tile != "settings")
+                            keyfile.remove_group (tile);
+                    }
+                    catch (KeyFileError error) { }
+                }
+
+                markup.append_printf ("""
+                    <div class="shortcut" id="s%u"><div class="preview new">
+                    <a class="add" href="#" onclick='return getAction("s%u");'></a>
+                    </div><div class="title">%s</div></div>
+                    """,
+                    slot_count + 1, slot_count + 1, _("Click to add a shortcut"));
+                markup.append_printf ("</div>\n</body>\n</html>\n");
+                return markup.str;
+            }
+
+            return null;
+        }
+    }
+}
+
index 190d273b82df45c45a39331ba4e068b77d6cfab2..9df799ee9385d97f4ee8c9c68deb06f51a289bbd 100644 (file)
@@ -4230,134 +4230,10 @@ prepare_speed_dial_html (MidoriView* view,
                          gboolean    load_missing)
 {
     MidoriBrowser* browser = midori_browser_get_for_widget (GTK_WIDGET (view));
-    GKeyFile* key_file;
-    GString* markup = NULL;
-    guint slot_count = 1, i, grid_index = 3, slot_size;
-    guint margin, div_factor;
-    gchar* speed_dial_head;
-    gchar* file_path;
-    gchar** groups;
-
-    g_object_get (browser, "speed-dial", &key_file, NULL);
-    file_path = midori_paths_get_res_filename ("speeddial-head.html");
-
-    if (key_file != NULL
-     && g_access (file_path, F_OK) == 0
-     && g_file_get_contents (file_path, &speed_dial_head, NULL, NULL))
-    {
-        gchar* header = sokoke_replace_variables (speed_dial_head,
-            "{title}", _("Speed Dial"),
-            "{click_to_add}", _("Click to add a shortcut"),
-            "{enter_shortcut_address}", _("Enter shortcut address"),
-            "{enter_shortcut_name}", _("Enter shortcut title"),
-            "{are_you_sure}", _("Are you sure you want to delete this shortcut?"),
-            NULL);
-
-        markup = g_string_new (header);
-
-        g_free (speed_dial_head);
-        g_free (file_path);
-        g_free (header);
-    }
-    else
-    {
-        g_free (file_path);
-        return NULL;
-    }
-
-    groups = g_key_file_get_groups (key_file, NULL);
-    for (i = 0; groups[i]; i++)
-    {
-        if (g_key_file_has_key (key_file, groups[i], "uri", NULL))
-           slot_count++;
-    }
-
-    /* try to guess the best X by X grid  size */
-    while ((grid_index * grid_index) < slot_count)
-        grid_index++;
-
-   /* percent width size of one slot */
-   slot_size = (100 / grid_index);
-   /* No editing in private/ app mode or without scripts */
-   g_string_append_printf (markup,
-        "%s<style>.cross { display:none }</style>%s"
-        "<style> div.shortcut { height: %d%%; width: %d%%; }</style>\n",
-        midori_paths_is_readonly () ? "" : "<noscript>",
-        midori_paths_is_readonly () ? "" : "</noscript>",
-        slot_size + 1, slot_size - 4);
-
-   /* Combined width of slots should always be less than 100%.
-    * Use half of the remaining percentage as a margin size */
-   if (slot_size * grid_index >= 100 && grid_index > 4)
-       div_factor = 8;
-   else
-       div_factor = 2;
-
-   margin = (100 - ((slot_size - 4) * grid_index)) / div_factor;
-
-   if (margin > 9)
-       margin = margin % 10;
-
-   g_string_append_printf (markup,
-        "<style> body { overflow:hidden } #content { margin-left: %d%%; }</style>",
-        margin);
-
-    if (katze_object_get_boolean (view->settings, "close-buttons-left"))
-        g_string_append_printf (markup,
-            "<style>.cross { left: -14px }</style>");
-
-    for (i = 0; groups[i]; i++)
-    {
-        gchar* uri = g_key_file_get_string (key_file, groups[i], "uri", NULL);
-        if (uri && strstr (uri, "://"))
-        {
-            gchar* title = g_key_file_get_string (key_file, groups[i], "title", NULL);
-            gchar* thumb_file = sokoke_build_thumbnail_path (uri);
-            gchar* encoded;
-            guint slot = atoi (groups[i] + strlen ("Dial "));
-
-            if (g_access (thumb_file, F_OK) == 0)
-            {
-                gsize sz;
-                gchar* thumb_content;
-                g_file_get_contents (thumb_file, &thumb_content, &sz, NULL);
-                encoded = g_base64_encode ((guchar*)thumb_content, sz);
-                g_free (thumb_content);
-            }
-            else
-            {
-                encoded = NULL;
-                if (load_missing)
-                    midori_view_speed_dial_get_thumb (view, groups[i], uri);
-            }
-            g_free (thumb_file);
-
-            g_string_append_printf (markup,
-                "<div class=\"shortcut\" id=\"s%d\"><div class=\"preview\">"
-                "<a class=\"cross\" href=\"#\" onclick='clearShortcut(\"s%d\");'></a>"
-                "<a href=\"%s\"><img src=\"data:image/png;base64,%s\" title='%s'></a>"
-                "</div><div class=\"title\" onclick='renameShortcut(\"s%d\");'>%s</div></div>\n",
-                slot, slot, uri, encoded ? encoded : "", title, slot, title ? title : "");
-
-            g_free (title);
-            g_free (encoded);
-        }
-        else if (strcmp (groups[i], "settings"))
-            g_key_file_remove_group (key_file, groups[i], NULL);
-
-        g_free (uri);
-    }
-    g_strfreev (groups);
-
-    g_string_append_printf (markup,
-        "<div class=\"shortcut\" id=\"s%d\"><div class=\"preview new\">"
-        "<a class=\"add\" href=\"#\" onclick='return getAction(\"s%d\");'></a>"
-        "</div><div class=\"title\">%s</div></div>\n",
-        slot_count + 1, slot_count + 1, _("Click to add a shortcut"));
-    g_string_append_printf (markup,
-            "</div>\n</body>\n</html>\n");
-
-    return g_string_free (markup, FALSE);
+    GKeyFile* key_file = katze_object_get_object (browser, "speed-dial");
+    return midori_speed_dial_get_html_fk (key_file,
+        katze_object_get_boolean (view->settings, "close-buttons-left"),
+        G_OBJECT (view), load_missing, NULL);
 }
 
 
index 5d3ca237a49b2add823d6195a6b60145e1dc6d5e..3d1cbd1fb02d01bde9440794e79ddcee37acd791 100644 (file)
@@ -64,15 +64,18 @@ sokoke_js_script_eval (JSContextRef js_context,
                        const gchar* script,
                        gchar**      exception)
 {
+    JSGlobalContextRef temporary_context = NULL;
     gchar* value;
     JSStringRef js_value_string;
     JSStringRef js_script;
     JSValueRef js_exception = NULL;
     JSValueRef js_value;
 
-    g_return_val_if_fail (js_context, FALSE);
     g_return_val_if_fail (script, FALSE);
 
+    if (!js_context)
+        js_context = temporary_context = JSGlobalContextCreateInGroup (NULL, NULL);
+
     js_script = JSStringCreateWithUTF8CString (script);
     js_value = JSEvaluateScript (js_context, js_script,
         JSContextGetGlobalObject (js_context), NULL, 0, &js_exception);
@@ -91,12 +94,16 @@ sokoke_js_script_eval (JSContextRef js_context,
             g_free (value);
         }
         JSStringRelease (js_message);
+        if (temporary_context)
+            JSGlobalContextRelease (temporary_context);
         return NULL;
     }
 
     js_value_string = JSValueToStringCopy (js_context, js_value, NULL);
     value = sokoke_js_string_utf8 (js_value_string);
     JSStringRelease (js_value_string);
+    if (temporary_context)
+        JSGlobalContextRelease (temporary_context);
     return value;
 }
 
diff --git a/tests/speeddial.vala b/tests/speeddial.vala
new file mode 100644 (file)
index 0000000..8a5d746
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2012 Christian Dywan <christian@twotoasts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ See the file COPYING for the full license text.
+*/
+string get_test_file (string contents) {
+    string file;
+    int fd = FileUtils.open_tmp ("speeddialXXXXXX", out file);
+    FileUtils.set_contents (file, contents, -1);
+    FileUtils.close (fd);
+    return file;
+}
+
+namespace Katze {
+    extern static string assert_str_equal (string input, string result, string expected);
+}
+
+static void speeddial_load () {
+    string data = get_test_file ("""
+            [Dial 1]
+            uri=http://example.com
+            title=Example
+            [settings]
+            columns=3
+            rows=3
+        """);
+
+    string json = get_test_file ("""
+            '{"shortcuts":[{"id":"s1","href":"http://example.com","title":"Example","img":"a2F0emU="}]}'
+        """);
+
+    var dial_data = new Midori.SpeedDial (data, "");
+    var dial_json = new Midori.SpeedDial ("", json);
+    FileUtils.remove (data);
+    FileUtils.remove (json);
+
+    Katze.assert_str_equal (json, dial_data.keyfile.to_data (), dial_json.keyfile.to_data ());
+    Katze.assert_str_equal (json, Midori.SpeedDial.get_next_free_slot_fk (dial_data.keyfile), "s2");
+    Katze.assert_str_equal (json, Midori.SpeedDial.get_next_free_Slot_fk (dial_json), "s2");
+}
+
+void main (string[] args) {
+    string temporary_cache = DirUtils.make_tmp ("cacheXXXXXX");
+    Environment.set_variable ("XDG_CACHE_HOME", temporary_cache, true);
+    Test.init (ref args);
+    Test.add_func ("/speeddial/load", speeddial_load);
+    Test.run ();
+    DirUtils.remove (temporary_cache);
+}
+