midori/midori/main.c
Christian Dywan 00d8621fe7 Unify toolbar rebuilding in _action_menubar_activate
And merge _midori_browser_save_toolbar_items into it,
which was already not used anywhere else.

CompactMenu is now always skipped and automatically
added if the menubar is hidden. So it can no longer be
duplicated or appear in the toolbar editor.

Fixes: https://bugs.launchpad.net/midori/+bug/903003
2011-12-14 00:03:02 +01:00

2618 lines
91 KiB
C

/*
Copyright (C) 2007-2009 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
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.
*/
#include "midori-app.h"
#include "midori-array.h"
#include "midori-bookmarks.h"
#include "midori-extension.h"
#include "midori-extensions.h"
#include "midori-history.h"
#include "midori-transfers.h"
#include "midori-panel.h"
#include "midori-platform.h"
#include "midori-preferences.h"
#include <midori/midori-core.h>
#include <config.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
#include <glib/gi18n-lib.h>
#include <webkit/webkit.h>
#include <sqlite3.h>
#if WEBKIT_CHECK_VERSION (1, 3, 11)
#define LIBSOUP_USE_UNSTABLE_REQUEST_API
#include <libsoup/soup-cache.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#if HAVE_HILDON
#define BOOKMARK_FILE "/home/user/.bookmarks/MyBookmarks.xml"
#else
#define BOOKMARK_FILE "bookmarks.xbel"
#endif
#ifdef HAVE_X11_EXTENSIONS_SCRNSAVER_H
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/scrnsaver.h>
#include <gdk/gdkx.h>
#endif
static gchar*
build_config_filename (const gchar* filename)
{
return g_build_filename (sokoke_set_config_dir (NULL), filename, NULL);
}
static MidoriWebSettings*
settings_and_accels_new (const gchar* config,
gchar*** extensions)
{
MidoriWebSettings* settings = midori_web_settings_new ();
gchar* config_file = g_build_filename (config, "config", NULL);
GKeyFile* key_file = g_key_file_new ();
GError* error = NULL;
GObjectClass* class;
guint i, n_properties;
GParamSpec** pspecs;
GParamSpec* pspec;
GType type;
const gchar* property;
gchar* str;
gint integer;
gfloat number;
gboolean boolean;
if (!g_key_file_load_from_file (key_file, config_file,
G_KEY_FILE_KEEP_COMMENTS, &error))
{
if (error->code == G_FILE_ERROR_NOENT)
{
GError* inner_error = NULL;
katze_assign (config_file, sokoke_find_config_filename (NULL, "config"));
g_key_file_load_from_file (key_file, config_file,
G_KEY_FILE_KEEP_COMMENTS, &inner_error);
if (inner_error != NULL)
{
printf (_("The configuration couldn't be loaded: %s\n"),
inner_error->message);
g_error_free (inner_error);
}
}
else
printf (_("The configuration couldn't be loaded: %s\n"),
error->message);
g_error_free (error);
}
class = G_OBJECT_GET_CLASS (settings);
pspecs = g_object_class_list_properties (class, &n_properties);
for (i = 0; i < n_properties; i++)
{
pspec = pspecs[i];
if (!(pspec->flags & G_PARAM_WRITABLE))
continue;
type = G_PARAM_SPEC_TYPE (pspec);
property = g_param_spec_get_name (pspec);
if (!g_key_file_has_key (key_file, "settings", property, NULL))
continue;
if (type == G_TYPE_PARAM_STRING)
{
str = g_key_file_get_string (key_file, "settings", property, NULL);
g_object_set (settings, property, str, NULL);
g_free (str);
}
else if (type == G_TYPE_PARAM_INT)
{
integer = g_key_file_get_integer (key_file, "settings", property, NULL);
g_object_set (settings, property, integer, NULL);
}
else if (type == G_TYPE_PARAM_FLOAT)
{
number = g_key_file_get_double (key_file, "settings", property, NULL);
g_object_set (settings, property, number, NULL);
}
else if (type == G_TYPE_PARAM_BOOLEAN)
{
boolean = g_key_file_get_boolean (key_file, "settings", property, NULL);
g_object_set (settings, property, boolean, NULL);
}
else if (type == G_TYPE_PARAM_ENUM)
{
GEnumClass* enum_class = G_ENUM_CLASS (
g_type_class_peek (pspec->value_type));
GEnumValue* enum_value;
str = g_key_file_get_string (key_file, "settings", property, NULL);
enum_value = g_enum_get_value_by_name (enum_class, str);
if (enum_value)
g_object_set (settings, property, enum_value->value, NULL);
else
g_warning (_("Value '%s' is invalid for %s"),
str, property);
g_free (str);
}
else
g_warning (_("Invalid configuration value '%s'"), property);
}
g_free (pspecs);
*extensions = g_key_file_get_keys (key_file, "extensions", NULL, NULL);
g_key_file_free (key_file);
/* Load accelerators */
katze_assign (config_file, g_build_filename (config, "accels", NULL));
if (g_access (config_file, F_OK) != 0)
katze_assign (config_file, sokoke_find_config_filename (NULL, "accels"));
gtk_accel_map_load (config_file);
g_free (config_file);
return settings;
}
static gboolean
settings_save_to_file (MidoriWebSettings* settings,
MidoriApp* app,
const gchar* filename,
GError** error)
{
GKeyFile* key_file;
GObjectClass* class;
guint i, n_properties;
GParamSpec** pspecs;
GParamSpec* pspec;
GType type;
const gchar* property;
gboolean saved;
KatzeArray* extensions = katze_object_get_object (app, "extensions");
MidoriExtension* extension;
gchar** _extensions;
key_file = g_key_file_new ();
class = G_OBJECT_GET_CLASS (settings);
pspecs = g_object_class_list_properties (class, &n_properties);
for (i = 0; i < n_properties; i++)
{
pspec = pspecs[i];
type = G_PARAM_SPEC_TYPE (pspec);
property = g_param_spec_get_name (pspec);
if (!(pspec->flags & G_PARAM_WRITABLE))
continue;
if (type == G_TYPE_PARAM_STRING)
{
gchar* string;
const gchar* def_string = G_PARAM_SPEC_STRING (pspec)->default_value;
if (!strcmp (property, "user-stylesheet-uri"))
{
const gchar* user_stylesheet_uri = g_object_get_data (G_OBJECT (settings), property);
if (user_stylesheet_uri)
{
g_key_file_set_string (key_file, "settings", property,
user_stylesheet_uri);
}
else
g_key_file_remove_key (key_file, "settings", property, NULL);
continue;
}
g_object_get (settings, property, &string, NULL);
if (!def_string)
def_string = "";
if (strcmp (string ? string : "", def_string))
g_key_file_set_string (key_file, "settings", property, string ? string : "");
g_free (string);
}
else if (type == G_TYPE_PARAM_INT)
{
gint integer;
g_object_get (settings, property, &integer, NULL);
if (integer != G_PARAM_SPEC_INT (pspec)->default_value)
g_key_file_set_integer (key_file, "settings", property, integer);
}
else if (type == G_TYPE_PARAM_FLOAT)
{
gfloat number;
g_object_get (settings, property, &number, NULL);
if (number != G_PARAM_SPEC_FLOAT (pspec)->default_value)
g_key_file_set_double (key_file, "settings", property, number);
}
else if (type == G_TYPE_PARAM_BOOLEAN)
{
gboolean truth;
g_object_get (settings, property, &truth, NULL);
if (truth != G_PARAM_SPEC_BOOLEAN (pspec)->default_value)
g_key_file_set_boolean (key_file, "settings", property, truth);
}
else if (type == G_TYPE_PARAM_ENUM)
{
GEnumClass* enum_class = G_ENUM_CLASS (
g_type_class_peek (pspec->value_type));
gint integer;
GEnumValue* enum_value;
g_object_get (settings, property, &integer, NULL);
enum_value = g_enum_get_value (enum_class, integer);
if (integer != G_PARAM_SPEC_ENUM (pspec)->default_value)
g_key_file_set_string (key_file, "settings", property,
enum_value->value_name);
}
else
g_warning (_("Invalid configuration value '%s'"), property);
}
g_free (pspecs);
if (extensions)
{
KATZE_ARRAY_FOREACH_ITEM (extension, extensions)
if (midori_extension_is_active (extension))
g_key_file_set_boolean (key_file, "extensions",
g_object_get_data (G_OBJECT (extension), "filename"), TRUE);
g_object_unref (extensions);
}
else if ((_extensions = g_object_get_data (G_OBJECT (app), "extensions")))
{
i = 0;
while (_extensions[i])
{
g_key_file_set_boolean (key_file, "extensions", _extensions[i], TRUE);
i++;
}
}
saved = sokoke_key_file_save_to_file (key_file, filename, error);
g_key_file_free (key_file);
return saved;
}
static KatzeArray*
search_engines_new_from_file (const gchar* filename,
GError** error)
{
KatzeArray* search_engines;
GKeyFile* key_file;
gchar** engines;
guint i, j, n_properties;
KatzeItem* item;
GParamSpec** pspecs;
const gchar* property;
gchar* value;
search_engines = katze_array_new (KATZE_TYPE_ITEM);
key_file = g_key_file_new ();
g_key_file_load_from_file (key_file, filename,
G_KEY_FILE_KEEP_COMMENTS, error);
/*g_key_file_load_from_data_dirs(keyFile, sFilename, NULL
, G_KEY_FILE_KEEP_COMMENTS, error);*/
engines = g_key_file_get_groups (key_file, NULL);
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (search_engines),
&n_properties);
for (i = 0; engines[i] != NULL; i++)
{
item = katze_item_new ();
for (j = 0; j < n_properties; j++)
{
if (!G_IS_PARAM_SPEC_STRING (pspecs[j]))
continue;
property = g_param_spec_get_name (pspecs[j]);
value = g_key_file_get_string (key_file, engines[i],
property, NULL);
g_object_set (item, property, value, NULL);
g_free (value);
}
katze_array_add_item (search_engines, item);
}
g_free (pspecs);
g_strfreev (engines);
g_key_file_free (key_file);
return search_engines;
}
static KatzeArray*
search_engines_new_from_folder (const gchar* config,
GString* error_messages)
{
gchar* config_file = g_build_filename (config, "search", NULL);
GError* error = NULL;
KatzeArray* search_engines;
search_engines = search_engines_new_from_file (config_file, &error);
/* We ignore for instance empty files */
if (error && (error->code == G_KEY_FILE_ERROR_PARSE
|| error->code == G_FILE_ERROR_NOENT))
{
g_error_free (error);
error = NULL;
}
if (!error && katze_array_is_empty (search_engines))
{
g_object_unref (search_engines);
#ifdef G_OS_WIN32
gchar* dir = g_win32_get_package_installation_directory_of_module (NULL);
katze_assign (config_file,
g_build_filename (dir, "etc", "xdg", PACKAGE_NAME, "search", NULL));
g_free (dir);
search_engines = search_engines_new_from_file (config_file, NULL);
#else
katze_assign (config_file,
sokoke_find_config_filename (NULL, "search"));
search_engines = search_engines_new_from_file (config_file, NULL);
#endif
}
else if (error)
{
if (error->code != G_FILE_ERROR_NOENT && error_messages)
g_string_append_printf (error_messages,
_("The search engines couldn't be loaded. %s\n"),
error->message);
g_error_free (error);
}
g_free (config_file);
return search_engines;
}
static gboolean
search_engines_save_to_file (KatzeArray* search_engines,
const gchar* filename,
GError** error)
{
GKeyFile* key_file;
guint j, n_properties;
KatzeItem* item;
const gchar* name;
GParamSpec** pspecs;
const gchar* property;
gchar* value;
gboolean saved;
key_file = g_key_file_new ();
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (search_engines),
&n_properties);
KATZE_ARRAY_FOREACH_ITEM (item, search_engines)
{
name = katze_item_get_name (item);
for (j = 0; j < n_properties; j++)
{
if (!G_IS_PARAM_SPEC_STRING (pspecs[j]))
continue;
property = g_param_spec_get_name (pspecs[j]);
g_object_get (item, property, &value, NULL);
if (value)
g_key_file_set_string (key_file, name, property, value);
g_free (value);
}
}
g_free (pspecs);
saved = sokoke_key_file_save_to_file (key_file, filename, error);
g_key_file_free (key_file);
return saved;
}
static void
midori_history_clear_cb (KatzeArray* array,
sqlite3* db)
{
char* errmsg = NULL;
if (sqlite3_exec (db, "DELETE FROM history; DELETE FROM search",
NULL, NULL, &errmsg) != SQLITE_OK)
{
g_printerr (_("Failed to clear history: %s\n"), errmsg);
sqlite3_free (errmsg);
}
}
static gboolean
midori_history_initialize (KatzeArray* array,
const gchar* filename,
const gchar* bookmarks_filename,
char** errmsg)
{
sqlite3* db;
gboolean has_day = FALSE;
sqlite3_stmt* stmt;
gint result;
gchar* sql;
if (sqlite3_open (filename, &db) != SQLITE_OK)
{
if (errmsg)
*errmsg = g_strdup_printf (_("Failed to open database: %s\n"),
sqlite3_errmsg (db));
sqlite3_close (db);
return FALSE;
}
sqlite3_exec (db, "PRAGMA journal_mode = TRUNCATE;", NULL, NULL, errmsg);
if (*errmsg)
{
g_warning ("Failed to set journal mode: %s", *errmsg);
sqlite3_free (*errmsg);
}
if (sqlite3_exec (db,
"CREATE TABLE IF NOT EXISTS "
"history (uri text, title text, date integer, day integer);"
"CREATE TABLE IF NOT EXISTS "
"search (keywords text, uri text, day integer);",
NULL, NULL, errmsg) != SQLITE_OK)
return FALSE;
sqlite3_prepare_v2 (db, "SELECT day FROM history LIMIT 1", -1, &stmt, NULL);
result = sqlite3_step (stmt);
if (result == SQLITE_ROW)
has_day = TRUE;
sqlite3_finalize (stmt);
if (!has_day)
sqlite3_exec (db,
"BEGIN TRANSACTION;"
"CREATE TEMPORARY TABLE backup (uri text, title text, date integer);"
"INSERT INTO backup SELECT uri,title,date FROM history;"
"DROP TABLE history;"
"CREATE TABLE history (uri text, title text, date integer, day integer);"
"INSERT INTO history SELECT uri,title,date,"
"julianday(date(date,'unixepoch','start of day','+1 day'))"
" - julianday('0001-01-01','start of day')"
"FROM backup;"
"DROP TABLE backup;"
"COMMIT;",
NULL, NULL, errmsg);
sql = g_strdup_printf ("ATTACH DATABASE '%s' AS bookmarks", bookmarks_filename);
sqlite3_exec (db, sql, NULL, NULL, errmsg);
g_free (sql);
g_object_set_data (G_OBJECT (array), "db", db);
g_signal_connect (array, "clear",
G_CALLBACK (midori_history_clear_cb), db);
return TRUE;
}
static void
midori_history_terminate (KatzeArray* array,
gint max_history_age)
{
sqlite3* db = g_object_get_data (G_OBJECT (array), "db");
char* errmsg = NULL;
gchar* sqlcmd = g_strdup_printf (
"DELETE FROM history WHERE "
"(julianday(date('now')) - julianday(date(date,'unixepoch')))"
" >= %d", max_history_age);
if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
{
/* i18n: Couldn't remove items that are older than n days */
g_printerr (_("Failed to remove old history items: %s\n"), errmsg);
sqlite3_free (errmsg);
}
g_free (sqlcmd);
sqlite3_close (db);
}
static void
midori_bookmarks_add_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db)
{
midori_bookmarks_insert_item_db (db, item,
katze_item_get_meta_string (item, "folder"));
}
static void
midori_bookmarks_remove_item_cb (KatzeArray* array,
KatzeItem* item,
sqlite3* db)
{
gchar* sqlcmd;
char* errmsg = NULL;
if (KATZE_ITEM_IS_BOOKMARK (item))
sqlcmd = sqlite3_mprintf (
"DELETE FROM bookmarks WHERE uri = '%q' "
" AND folder = '%q'",
katze_item_get_uri (item),
katze_item_get_meta_string (item, "folder"));
else
sqlcmd = sqlite3_mprintf (
"DELETE FROM bookmarks WHERE title = '%q'"
" AND folder = '%q'",
katze_item_get_name (item),
katze_item_get_meta_string (item, "folder"));
if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
{
g_printerr (_("Failed to remove history item: %s\n"), errmsg);
sqlite3_free (errmsg);
}
sqlite3_free (sqlcmd);
}
static sqlite3*
midori_bookmarks_initialize (KatzeArray* array,
const gchar* filename,
char** errmsg)
{
sqlite3* db;
if (sqlite3_open (filename, &db) != SQLITE_OK)
{
if (errmsg)
*errmsg = g_strdup_printf (_("Failed to open database: %s\n"),
sqlite3_errmsg (db));
sqlite3_close (db);
return NULL;
}
if (sqlite3_exec (db,
"CREATE TABLE IF NOT EXISTS "
"bookmarks (uri text, title text, folder text, "
"desc text, app integer, toolbar integer);",
NULL, NULL, errmsg) != SQLITE_OK)
return NULL;
g_signal_connect (array, "add-item",
G_CALLBACK (midori_bookmarks_add_item_cb), db);
g_signal_connect (array, "remove-item",
G_CALLBACK (midori_bookmarks_remove_item_cb), db);
return db;
}
static void
midori_bookmarks_import (const gchar* filename,
sqlite3* db)
{
KatzeArray* bookmarks;
GError* error = NULL;
bookmarks = katze_array_new (KATZE_TYPE_ARRAY);
if (!midori_array_from_file (bookmarks, filename, "xbel", &error))
{
g_warning (_("The bookmarks couldn't be saved. %s"), error->message);
g_error_free (error);
return;
}
midori_bookmarks_import_array_db (db, bookmarks, "");
}
static void
settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
MidoriApp* app)
{
GError* error = NULL;
gchar* config_file;
/* Skip state related properties to avoid disk IO */
if (pspec && pspec->flags & MIDORI_PARAM_DELAY_SAVING)
return;
config_file = build_config_filename ("config");
if (!settings_save_to_file (settings, app, config_file, &error))
{
g_warning (_("The configuration couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
extension_activate_cb (MidoriExtension* extension,
MidoriApp* app)
{
MidoriWebSettings* settings = katze_object_get_object (app, "settings");
settings_notify_cb (settings, NULL, app);
g_object_unref (settings);
}
static void
accel_map_changed_cb (GtkAccelMap* accel_map,
gchar* accel_path,
guint accel_key,
GdkModifierType accel_mods)
{
gchar* config_file = build_config_filename ("accels");
gtk_accel_map_save (config_file);
g_free (config_file);
}
static void
midori_search_engines_modify_cb (KatzeArray* array,
gpointer item,
KatzeArray* search_engines)
{
gchar* config_file = build_config_filename ("search");
GError* error = NULL;
if (!search_engines_save_to_file (search_engines, config_file, &error))
{
g_warning (_("The search engines couldn't be saved. %s"),
error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
midori_search_engines_move_item_cb (KatzeArray* array,
gpointer item,
gint position,
KatzeArray* search_engines)
{
midori_search_engines_modify_cb (array, item, search_engines);
}
static void
midori_trash_add_item_no_save_cb (KatzeArray* trash,
GObject* item)
{
if (katze_array_get_nth_item (trash, 10))
{
KatzeItem* obsolete_item = katze_array_get_nth_item (trash, 0);
katze_array_remove_item (trash, obsolete_item);
}
}
static void
midori_trash_remove_item_cb (KatzeArray* trash,
GObject* item)
{
gchar* config_file = build_config_filename ("tabtrash.xbel");
GError* error = NULL;
midori_trash_add_item_no_save_cb (trash, item);
if (!midori_array_to_file (trash, config_file, "xbel", &error))
{
/* i18n: Trash, or wastebin, containing closed tabs */
g_warning (_("The trash couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
midori_trash_add_item_cb (KatzeArray* trash,
GObject* item)
{
midori_trash_remove_item_cb (trash, item);
}
static void
midori_browser_show_preferences_cb (MidoriBrowser* browser,
KatzePreferences* preferences,
MidoriApp* app)
{
KatzeArray* array;
GtkWidget* scrolled;
GtkWidget* addon;
GList* children;
GtkWidget* page;
/* Hide if there are no extensions at all */
array = katze_object_get_object (app, "extensions");
if (!katze_array_get_nth_item (array, 0))
{
g_object_unref (array);
return;
}
g_object_unref (array);
scrolled = g_object_new (KATZE_TYPE_SCROLLED, "visible", TRUE, NULL);
/* For lack of a better way of keeping descriptions visible */
g_object_set (scrolled, "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
addon = g_object_new (MIDORI_TYPE_EXTENSIONS, "app", app, NULL);
children = gtk_container_get_children (GTK_CONTAINER (addon));
gtk_widget_reparent (g_list_nth_data (children, 0), scrolled);
g_list_free (children);
page = katze_preferences_add_category (preferences,
_("Extensions"), STOCK_EXTENSIONS);
gtk_box_pack_start (GTK_BOX (page), scrolled, TRUE, TRUE, 4);
}
static void
midori_browser_privacy_preferences_cb (MidoriBrowser* browser,
KatzePreferences* preferences,
MidoriApp* app)
{
MidoriWebSettings* settings = midori_browser_get_settings (browser);
GtkWidget* button;
GtkWidget* label;
gchar* markup;
katze_preferences_add_category (preferences, _("Privacy"), GTK_STOCK_INDEX);
katze_preferences_add_group (preferences, NULL);
button = katze_property_label (settings, "maximum-cookie-age");
katze_preferences_add_widget (preferences, button, "indented");
button = katze_property_proxy (settings, "maximum-cookie-age", "days");
katze_preferences_add_widget (preferences, button, "spanned");
#ifdef HAVE_LIBSOUP_2_29_91
button = katze_property_proxy (settings, "first-party-cookies-only", NULL);
katze_preferences_add_widget (preferences, button, "filled");
#endif
markup = g_strdup_printf ("<span size=\"smaller\">%s</span>",
_("Cookies store login data, saved games, "
"or user profiles for advertisement purposes."));
label = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (label), markup);
g_free (markup);
katze_preferences_add_widget (preferences, label, "filled");
button = katze_property_proxy (settings, "enable-offline-web-application-cache", NULL);
katze_preferences_add_widget (preferences, button, "indented");
button = katze_property_proxy (settings, "enable-html5-local-storage", NULL);
katze_preferences_add_widget (preferences, button, "spanned");
button = katze_property_proxy (settings, "strip-referer", NULL);
katze_preferences_add_widget (preferences, button, "indented");
katze_preferences_add_widget (preferences, gtk_label_new (NULL), "indented");
button = katze_property_label (settings, "maximum-history-age");
katze_preferences_add_widget (preferences, button, "indented");
button = katze_property_proxy (settings, "maximum-history-age", "days");
katze_preferences_add_widget (preferences, button, "spanned");
}
static void
midori_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
KatzeNet* net)
{
GtkWidget* panel;
GtkWidget* addon;
panel = katze_object_get_object (browser, "panel");
addon = g_object_new (MIDORI_TYPE_BOOKMARKS, "app", app, "visible", TRUE, NULL);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
addon = g_object_new (MIDORI_TYPE_HISTORY, "app", app, "visible", TRUE, NULL);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
addon = g_object_new (MIDORI_TYPE_TRANSFERS, "app", app, "visible", TRUE, NULL);
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
/* Extensions */
g_signal_connect (browser, "show-preferences",
G_CALLBACK (midori_browser_privacy_preferences_cb), app);
g_signal_connect (browser, "show-preferences",
G_CALLBACK (midori_browser_show_preferences_cb), app);
g_object_unref (panel);
}
static guint save_timeout = 0;
static gboolean
midori_session_save_timeout_cb (KatzeArray* session)
{
gchar* config_file = build_config_filename ("session.xbel");
GError* error = NULL;
if (!midori_array_to_file (session, config_file, "xbel", &error))
{
g_warning (_("The session couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
save_timeout = 0;
g_object_unref (session);
return FALSE;
}
static void
midori_browser_session_cb (MidoriBrowser* browser,
gpointer pspec,
KatzeArray* session)
{
if (!save_timeout)
{
g_object_ref (session);
save_timeout = g_timeout_add_seconds (5,
(GSourceFunc)midori_session_save_timeout_cb, session);
}
}
static void
midori_app_quit_cb (MidoriBrowser* browser,
KatzeArray* session)
{
gchar* config_file = build_config_filename ("running");
g_unlink (config_file);
g_free (config_file);
if (session)
midori_session_save_timeout_cb (session);
}
static void
midori_browser_weak_notify_cb (MidoriBrowser* browser,
KatzeArray* session)
{
g_object_disconnect (browser, "any-signal",
G_CALLBACK (midori_browser_session_cb), session, NULL);
}
static void
midori_soup_session_set_proxy_uri (SoupSession* session,
const gchar* uri)
{
gchar* fixed_uri;
SoupURI* proxy_uri;
/* soup_uri_new expects a non-NULL string with a protocol */
if (midori_uri_is_http (uri))
proxy_uri = soup_uri_new (uri);
else if (uri && *uri)
{
fixed_uri = g_strconcat ("http://", uri, NULL);
proxy_uri = soup_uri_new (fixed_uri);
g_free (fixed_uri);
}
else
proxy_uri = NULL;
g_object_set (session, "proxy-uri", proxy_uri, NULL);
if (proxy_uri)
soup_uri_free (proxy_uri);
}
static void
soup_session_settings_notify_http_proxy_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
SoupSession* session)
{
MidoriProxy proxy_type;
proxy_type = katze_object_get_enum (settings, "proxy-type");
if (proxy_type == MIDORI_PROXY_AUTOMATIC)
{
gboolean gnome_supported = FALSE;
GModule* module;
GType (*get_type_function) (void);
if (g_module_supported ())
if ((module = g_module_open ("libsoup-gnome-2.4.so", G_MODULE_BIND_LOCAL)))
{
if (g_module_symbol (module, "soup_proxy_resolver_gnome_get_type",
(void*) &get_type_function))
{
soup_session_add_feature_by_type (session, get_type_function ());
gnome_supported = TRUE;
}
}
if (!gnome_supported)
midori_soup_session_set_proxy_uri (session, g_getenv ("http_proxy"));
}
else if (proxy_type == MIDORI_PROXY_HTTP)
{
gchar* proxy = katze_object_get_string (settings, "http-proxy");
GString *http_proxy = g_string_new (proxy);
g_string_append_printf (http_proxy, ":%d", katze_object_get_int (settings, "http-proxy-port"));
midori_soup_session_set_proxy_uri (session, http_proxy->str);
g_string_free (http_proxy, TRUE);
g_free (proxy);
}
else
midori_soup_session_set_proxy_uri (session, NULL);
}
#ifdef HAVE_LIBSOUP_2_29_91
static void
soup_session_settings_notify_first_party_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
SoupSession* session)
{
void* jar = soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
gboolean yes = katze_object_get_boolean (settings, "first-party-cookies-only");
g_object_set (jar, "accept-policy",
yes ? SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
: SOUP_COOKIE_JAR_ACCEPT_ALWAYS, NULL);
}
#endif
static void
midori_soup_session_settings_accept_language_cb (SoupSession* session,
SoupMessage* msg,
MidoriWebSettings* settings)
{
gchar* languages = katze_object_get_string (settings, "preferred-languages");
gchar* accpt;
/* Empty, use the system locales */
if (!(languages && *languages))
accpt = sokoke_accept_languages (g_get_language_names ());
/* No =, no ., looks like a list of language names */
else if (!(strchr (languages, '=') && strchr (languages, '.')))
{
gchar ** lang_names = g_strsplit_set (languages, ",; ", -1);
accpt = sokoke_accept_languages ((const gchar* const *)lang_names);
g_strfreev (lang_names);
}
/* Presumably a well formatted list including priorities */
else
accpt = languages;
if (accpt != languages)
g_free (languages);
soup_message_headers_append (msg->request_headers, "Accept-Language", accpt);
g_free (accpt);
if (katze_object_get_boolean (settings, "strip-referer"))
{
const gchar* referer
= soup_message_headers_get_one (msg->request_headers, "Referer");
SoupURI* destination = soup_message_get_uri (msg);
if (referer && destination && !strstr (referer, destination->host))
{
SoupURI* stripped_uri = soup_uri_new (referer);
gchar* stripped_referer;
soup_uri_set_path (stripped_uri, NULL);
soup_uri_set_query (stripped_uri, NULL);
stripped_referer = soup_uri_to_string (stripped_uri, FALSE);
soup_uri_free (stripped_uri);
if (g_getenv ("MIDORI_SOUP_DEBUG"))
g_message ("Referer stripped");
soup_message_headers_replace (msg->request_headers, "Referer",
stripped_referer);
g_free (stripped_referer);
}
}
}
static void
midori_soup_session_debug (SoupSession* session)
{
const char* soup_debug = g_getenv ("MIDORI_SOUP_DEBUG");
if (soup_debug)
{
gint soup_debug_level = atoi (soup_debug);
SoupLogger* logger = soup_logger_new (soup_debug_level, -1);
soup_logger_attach (logger, session);
g_object_unref (logger);
}
}
static gboolean
midori_load_soup_session (gpointer settings)
{
SoupSession* session = webkit_get_default_session ();
#if defined (HAVE_LIBSOUP_2_37_1)
g_object_set (session,
"ssl-use-system-ca-file", TRUE,
"ssl-strict", FALSE,
NULL);
#elif defined (HAVE_LIBSOUP_2_29_91)
const gchar* certificate_files[] =
{
"/etc/pki/tls/certs/ca-bundle.crt",
"/etc/ssl/certs/ca-certificates.crt",
"/etc/ssl/certs/ca-bundle.crt",
"/usr/local/share/certs/ca-root-nss.crt", /* FreeBSD */
"/var/lib/ca-certificates/ca-bundle.pem", /* openSUSE */
NULL
};
guint i;
for (i = 0; i < G_N_ELEMENTS (certificate_files); i++)
if (g_access (certificate_files[i], F_OK) == 0)
{
g_object_set (session,
"ssl-ca-file", certificate_files[i],
"ssl-strict", FALSE,
NULL);
break;
}
if (i == G_N_ELEMENTS (certificate_files))
g_warning (_("No root certificate file is available. "
"SSL certificates cannot be verified."));
#endif
#if !WEBKIT_CHECK_VERSION (1, 3, 5)
/* See http://stevesouders.com/ua/index.php */
g_object_set (session, "max-conns", 60,
"max-conns-per-host", 6,
NULL);
#endif
soup_session_settings_notify_http_proxy_cb (settings, NULL, session);
g_signal_connect (settings, "notify::http-proxy",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), session);
g_signal_connect (settings, "notify::proxy-type",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), session);
#ifdef HAVE_LIBSOUP_2_29_91
if (g_object_class_find_property (G_OBJECT_GET_CLASS (settings),
"enable-file-access-from-file-uris")) /* WebKitGTK+ >= 1.1.21 */
g_signal_connect (settings, "notify::first-party-cookies-only",
G_CALLBACK (soup_session_settings_notify_first_party_cb), session);
#endif
g_signal_connect (session, "request-queued",
G_CALLBACK (midori_soup_session_settings_accept_language_cb), settings);
midori_soup_session_debug (session);
g_object_set_data (G_OBJECT (session), "midori-session-initialized", (void*)1);
return FALSE;
}
static void
button_modify_preferences_clicked_cb (GtkWidget* button,
MidoriWebSettings* settings)
{
GtkWidget* dialog = midori_preferences_new (
GTK_WINDOW (gtk_widget_get_toplevel (button)), settings);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_DELETE_EVENT)
gtk_widget_destroy (dialog);
}
static void
button_disable_extensions_clicked_cb (GtkWidget* button,
MidoriApp* app)
{
g_object_set_data (G_OBJECT (app), "extensions", NULL);
gtk_widget_set_sensitive (button, FALSE);
}
static MidoriStartup
midori_show_diagnostic_dialog (MidoriWebSettings* settings,
KatzeArray* _session)
{
GtkWidget* dialog;
GtkWidget* content_area;
GdkScreen* screen;
GtkIconTheme* icon_theme;
GtkWidget* align;
GtkWidget* box;
GtkWidget* button;
MidoriApp* app = katze_item_get_parent (KATZE_ITEM (_session));
MidoriStartup load_on_startup = katze_object_get_enum (settings, "load-on-startup");
gint response;
dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
_("Midori seems to have crashed the last time it was opened. "
"If this happened repeatedly, try one of the following options "
"to solve the problem."));
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
gtk_window_set_title (GTK_WINDOW (dialog), g_get_application_name ());
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
screen = gtk_widget_get_screen (dialog);
if (screen)
{
icon_theme = gtk_icon_theme_get_for_screen (screen);
if (gtk_icon_theme_has_icon (icon_theme, "midori"))
gtk_window_set_icon_name (GTK_WINDOW (dialog), "midori");
else
gtk_window_set_icon_name (GTK_WINDOW (dialog), "web-browser");
}
align = gtk_alignment_new (0.5, 0.5, 0.5, 0.5);
gtk_container_add (GTK_CONTAINER (content_area), align);
box = gtk_hbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (align), box);
button = gtk_button_new_with_mnemonic (_("Modify _preferences"));
g_signal_connect (button, "clicked",
G_CALLBACK (button_modify_preferences_clicked_cb), settings);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
button = gtk_button_new_with_mnemonic (_("Disable all _extensions"));
if (g_object_get_data (G_OBJECT (app), "extensions"))
g_signal_connect (button, "clicked",
G_CALLBACK (button_disable_extensions_clicked_cb), app);
else
gtk_widget_set_sensitive (button, FALSE);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
gtk_widget_show_all (align);
button = katze_property_proxy (settings, "show-crash-dialog", NULL);
gtk_widget_show (button);
gtk_container_add (GTK_CONTAINER (content_area), button);
gtk_container_set_focus_child (GTK_CONTAINER (dialog), gtk_dialog_get_action_area (GTK_DIALOG (dialog)));
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
_("Discard old tabs"), MIDORI_STARTUP_BLANK_PAGE,
_("Show last tabs without loading"), MIDORI_STARTUP_DELAYED_PAGES,
_("Show last open tabs"), MIDORI_STARTUP_LAST_OPEN_PAGES,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog),
load_on_startup == MIDORI_STARTUP_HOMEPAGE
? MIDORI_STARTUP_BLANK_PAGE : load_on_startup);
if (1)
{
/* GtkLabel can't wrap the text properly. Until some day
this works, we implement this hack to do it ourselves. */
GtkWidget* hbox;
GtkWidget* vbox;
GtkWidget* label;
GList* ch;
GtkRequisition req;
ch = gtk_container_get_children (GTK_CONTAINER (content_area));
hbox = (GtkWidget*)g_list_nth_data (ch, 0);
g_list_free (ch);
ch = gtk_container_get_children (GTK_CONTAINER (hbox));
vbox = (GtkWidget*)g_list_nth_data (ch, 1);
g_list_free (ch);
ch = gtk_container_get_children (GTK_CONTAINER (vbox));
label = (GtkWidget*)g_list_nth_data (ch, 0);
g_list_free (ch);
gtk_widget_size_request (content_area, &req);
gtk_widget_set_size_request (label, req.width * 0.9, -1);
}
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_DELETE_EVENT)
response = G_MAXINT;
else if (response == MIDORI_STARTUP_BLANK_PAGE)
katze_array_clear (_session);
return response;
}
static gboolean
midori_load_soup_session_full (gpointer settings)
{
SoupSession* session = webkit_get_default_session ();
SoupCookieJar* jar;
gchar* config_file;
SoupSessionFeature* feature;
gboolean have_new_cookies;
SoupSessionFeature* feature_import;
midori_load_soup_session (settings);
config_file = build_config_filename ("logins");
feature = g_object_new (KATZE_TYPE_HTTP_AUTH, "filename", config_file, NULL);
soup_session_add_feature (session, feature);
g_object_unref (feature);
jar = soup_cookie_jar_new ();
g_object_set_data (G_OBJECT (jar), "midori-settings", settings);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (jar));
g_object_unref (jar);
katze_assign (config_file, build_config_filename ("cookies.db"));
have_new_cookies = g_access (config_file, F_OK) == 0;
feature = g_object_new (KATZE_TYPE_HTTP_COOKIES_SQLITE, NULL);
g_object_set_data_full (G_OBJECT (feature), "filename",
config_file, (GDestroyNotify)g_free);
soup_session_add_feature (session, feature);
g_object_unref (feature);
if (!have_new_cookies)
{
katze_assign (config_file, build_config_filename ("cookies.txt"));
if (g_access (config_file, F_OK) == 0)
{
g_message ("Importing cookies from txt to sqlite3");
feature_import = g_object_new (KATZE_TYPE_HTTP_COOKIES, NULL);
g_object_set_data_full (G_OBJECT (feature_import), "filename",
config_file, (GDestroyNotify)g_free);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (feature_import));
soup_session_remove_feature (session, SOUP_SESSION_FEATURE (feature_import));
}
}
#if WEBKIT_CHECK_VERSION (1, 3, 11)
katze_assign (config_file, g_build_filename (g_get_user_cache_dir (),
PACKAGE_NAME, "web", NULL));
feature = SOUP_SESSION_FEATURE (soup_cache_new (config_file, 0));
soup_session_add_feature (session, feature);
soup_cache_set_max_size (SOUP_CACHE (feature),
katze_object_get_int (settings, "maximum-cache-size") * 1024 * 1024);
soup_cache_load (SOUP_CACHE (feature));
#endif
g_free (config_file);
return FALSE;
}
static gboolean
midori_load_extensions (gpointer data)
{
MidoriApp* app = MIDORI_APP (data);
gchar** active_extensions = g_object_get_data (G_OBJECT (app), "extensions");
KatzeArray* extensions;
#ifdef G_ENABLE_DEBUG
gboolean startup_timer = g_getenv ("MIDORI_STARTTIME") != NULL;
GTimer* timer = startup_timer ? g_timer_new () : NULL;
#endif
/* Load extensions */
extensions = katze_array_new (MIDORI_TYPE_EXTENSION);
g_object_set (app, "extensions", extensions, NULL);
if (g_module_supported ())
{
gchar* extension_path;
GDir* extension_dir = NULL;
if (!(extension_path = g_strdup (g_getenv ("MIDORI_EXTENSION_PATH"))))
extension_path = sokoke_find_lib_path (PACKAGE_NAME);
if (extension_path != NULL)
extension_dir = g_dir_open (extension_path, 0, NULL);
if (extension_dir != NULL)
{
const gchar* filename;
while ((filename = g_dir_read_name (extension_dir)))
{
gchar* fullname;
GModule* module;
typedef MidoriExtension* (*extension_init_func)(void);
extension_init_func extension_init;
MidoriExtension* extension = NULL;
/* Ignore files which don't have the correct suffix */
if (!g_str_has_suffix (filename, G_MODULE_SUFFIX))
continue;
fullname = g_build_filename (extension_path, filename, NULL);
module = g_module_open (fullname, G_MODULE_BIND_LOCAL);
g_free (fullname);
if (module && g_module_symbol (module, "extension_init",
(gpointer) &extension_init))
{
extension = extension_init ();
if (extension != NULL)
{
/* Signal that we want the extension to load and save */
g_object_set_data_full (G_OBJECT (extension), "filename",
g_strdup (filename), g_free);
if (midori_extension_is_prepared (extension))
midori_extension_get_config_dir (extension);
}
}
if (!extension)
{
/* No extension, no error: not available, not shown */
if (g_module_error () == NULL)
continue;
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", filename,
"description", g_module_error (),
NULL);
g_warning ("%s", g_module_error ());
}
katze_array_add_item (extensions, extension);
if (active_extensions)
{
guint i = 0;
gchar* name;
while ((name = active_extensions[i++]))
if (!g_strcmp0 (filename, name))
g_signal_emit_by_name (extension, "activate", app);
}
g_signal_connect_after (extension, "activate",
G_CALLBACK (extension_activate_cb), app);
g_signal_connect_after (extension, "deactivate",
G_CALLBACK (extension_activate_cb), app);
g_object_unref (extension);
}
g_dir_close (extension_dir);
}
g_free (extension_path);
}
g_strfreev (active_extensions);
#ifdef G_ENABLE_DEBUG
if (startup_timer)
g_debug ("Extensions:\t%f", g_timer_elapsed (timer, NULL));
#endif
return FALSE;
}
static void
midori_browser_action_last_session_activate_cb (GtkAction* action,
MidoriBrowser* browser)
{
KatzeArray* old_session = katze_array_new (KATZE_TYPE_ITEM);
gchar* config_file = build_config_filename ("session.old.xbel");
GError* error = NULL;
if (midori_array_from_file (old_session, config_file, "xbel", &error))
{
KatzeItem* item;
KATZE_ARRAY_FOREACH_ITEM (item, old_session)
midori_browser_add_item (browser, item);
}
else
{
g_warning (_("The session couldn't be loaded: %s\n"), error->message);
/* FIXME: Show a graphical dialog */
g_error_free (error);
}
g_free (config_file);
gtk_action_set_sensitive (action, FALSE);
g_signal_handlers_disconnect_by_func (action,
midori_browser_action_last_session_activate_cb, browser);
}
static gboolean
midori_load_session (gpointer data)
{
KatzeArray* _session = KATZE_ARRAY (data);
MidoriBrowser* browser;
MidoriApp* app = katze_item_get_parent (KATZE_ITEM (_session));
MidoriWebSettings* settings = katze_object_get_object (app, "settings");
MidoriStartup load_on_startup;
gchar* config_file;
KatzeArray* session;
KatzeItem* item;
gint64 current;
gchar** command = g_object_get_data (G_OBJECT (app), "execute-command");
#ifdef G_ENABLE_DEBUG
gboolean startup_timer = g_getenv ("MIDORI_STARTTIME") != NULL;
GTimer* timer = startup_timer ? g_timer_new () : NULL;
#endif
browser = midori_app_create_browser (app);
g_signal_connect_after (katze_object_get_object (app, "settings"), "notify",
G_CALLBACK (settings_notify_cb), app);
config_file = build_config_filename ("session.old.xbel");
if (g_access (config_file, F_OK) == 0)
{
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action = gtk_action_group_get_action (action_group, "LastSession");
g_signal_connect (action, "activate",
G_CALLBACK (midori_browser_action_last_session_activate_cb), browser);
gtk_action_set_visible (action, TRUE);
}
midori_app_add_browser (app, browser);
gtk_widget_show (GTK_WIDGET (browser));
katze_assign (config_file, build_config_filename ("accels"));
g_signal_connect_after (gtk_accel_map_get (), "changed",
G_CALLBACK (accel_map_changed_cb), NULL);
load_on_startup = (MidoriStartup)g_object_get_data (G_OBJECT (settings), "load-on-startup");
if (katze_array_is_empty (_session))
{
gchar* homepage;
item = katze_item_new ();
if (load_on_startup == MIDORI_STARTUP_BLANK_PAGE)
katze_item_set_uri (item, "");
else
{
g_object_get (settings, "homepage", &homepage, NULL);
katze_item_set_uri (item, homepage);
g_free (homepage);
}
katze_array_add_item (_session, item);
g_object_unref (item);
}
session = midori_browser_get_proxy_array (browser);
KATZE_ARRAY_FOREACH_ITEM (item, _session)
{
katze_item_set_meta_integer (item, "append", 1);
katze_item_set_meta_integer (item, "dont-write-history", 1);
if (load_on_startup == MIDORI_STARTUP_DELAYED_PAGES)
katze_item_set_meta_integer (item, "delay", 1);
midori_browser_add_item (browser, item);
}
current = katze_item_get_meta_integer (KATZE_ITEM (_session), "current");
if (!(item = katze_array_get_nth_item (_session, current)))
{
current = 0;
item = katze_array_get_nth_item (_session, 0);
}
midori_browser_set_current_page (browser, current);
if (midori_uri_is_blank (katze_item_get_uri (item)))
midori_browser_activate_action (browser, "Location");
g_object_unref (settings);
g_object_unref (_session);
katze_assign (config_file, build_config_filename ("session.xbel"));
g_signal_connect_after (browser, "add-tab",
G_CALLBACK (midori_browser_session_cb), session);
g_signal_connect_after (browser, "remove-tab",
G_CALLBACK (midori_browser_session_cb), session);
g_signal_connect (app, "quit",
G_CALLBACK (midori_app_quit_cb), session);
g_object_weak_ref (G_OBJECT (session),
(GWeakNotify)(midori_browser_weak_notify_cb), browser);
if (command)
midori_app_send_command (app, command);
#ifdef G_ENABLE_DEBUG
if (startup_timer)
g_debug ("Session setup:\t%f", g_timer_elapsed (timer, NULL));
#endif
return FALSE;
}
#define HAVE_OFFSCREEN GTK_CHECK_VERSION (2, 20, 0)
static void
snapshot_load_finished_cb (GtkWidget* web_view,
WebKitWebFrame* web_frame,
gchar* filename)
{
#if HAVE_OFFSCREEN
GdkPixbuf* pixbuf = gtk_offscreen_window_get_pixbuf (GTK_OFFSCREEN_WINDOW (
gtk_widget_get_parent (web_view)));
gdk_pixbuf_save (pixbuf, filename, "png", NULL, "compression", "7", NULL);
g_object_unref (pixbuf);
#else
GError* error;
GtkPrintOperation* operation = gtk_print_operation_new ();
gtk_print_operation_set_export_filename (operation, filename);
error = NULL;
webkit_web_frame_print_full (web_frame, operation,
GTK_PRINT_OPERATION_ACTION_EXPORT, &error);
if (error != NULL)
{
g_error ("%s", error->message);
gtk_main_quit ();
}
g_object_unref (operation);
#endif
g_print (_("Snapshot saved to: %s\n"), filename);
gtk_main_quit ();
}
static void
midori_web_app_browser_notify_load_status_cb (MidoriBrowser* browser,
GParamSpec* pspec,
gpointer data)
{
if (katze_object_get_enum (browser, "load-status") != MIDORI_LOAD_PROVISIONAL)
{
GtkWidget* view = midori_browser_get_current_tab (browser);
GdkPixbuf* icon = midori_view_get_icon (MIDORI_VIEW (view));
if (midori_view_is_blank (MIDORI_VIEW (view)))
icon = NULL;
gtk_window_set_icon (GTK_WINDOW (browser), icon);
}
}
static MidoriBrowser*
midori_web_app_browser_new_window_cb (MidoriBrowser* browser,
MidoriBrowser* new_browser,
gpointer user_data)
{
if (new_browser == NULL)
new_browser = midori_browser_new ();
g_object_set (new_browser,
"settings", midori_browser_get_settings (browser),
NULL);
gtk_widget_show (GTK_WIDGET (new_browser));
return new_browser;
}
static void
midori_remove_config_file (gint clear_prefs,
gint flag,
const gchar* filename)
{
if ((clear_prefs & flag) == flag)
{
gchar* config_file = build_config_filename (filename);
g_unlink (config_file);
g_free (config_file);
}
}
static gchar*
midori_prepare_uri (const gchar *uri)
{
gchar* uri_ready;
if (g_path_is_absolute (uri))
return g_filename_to_uri (uri, NULL, NULL);
else if (g_str_has_prefix(uri, "javascript:"))
return NULL;
else if (g_file_test (uri, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
{
gchar* current_dir = g_get_current_dir ();
uri_ready = g_strconcat ("file://", current_dir,
G_DIR_SEPARATOR_S, uri, NULL);
g_free (current_dir);
return uri_ready;
}
return sokoke_magic_uri (uri);
}
#ifdef HAVE_SIGNAL_H
static void
signal_handler (int signal_id)
{
signal (signal_id, 0);
midori_app_quit_cb (NULL, NULL);
if (kill (getpid (), signal_id))
exit (1);
}
#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 (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, g_get_user_cache_dir (),
PACKAGE_NAME, "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,
gchar* blocked_uris)
{
static GRegex* regex = NULL;
SoupURI* soup_uri;
gchar* uri;
if (!regex)
regex = g_regex_new (blocked_uris, 0, 0, NULL);
soup_uri = soup_message_get_uri (msg);
uri = soup_uri_to_string (soup_uri, FALSE);
if (g_regex_match (regex, uri, 0, 0))
{
soup_uri = soup_uri_new ("http://.invalid");
soup_message_set_uri (msg, soup_uri);
soup_uri_free (soup_uri);
}
g_free (uri);
}
typedef struct {
MidoriBrowser* browser;
guint timeout;
gchar* uri;
} MidoriInactivityTimeout;
static gboolean
midori_inactivity_timeout (gpointer data)
{
#ifdef HAVE_X11_EXTENSIONS_SCRNSAVER_H
MidoriInactivityTimeout* mit = data;
static Display* xdisplay = NULL;
static XScreenSaverInfo* mit_info = NULL;
static int has_extension = -1;
int event_base, error_base;
if (has_extension == -1)
{
GdkDisplay* display = gtk_widget_get_display (GTK_WIDGET (mit->browser));
xdisplay = GDK_DISPLAY_XDISPLAY (display);
has_extension = XScreenSaverQueryExtension (xdisplay,
&event_base, &error_base);
}
if (has_extension)
{
if (!mit_info)
mit_info = XScreenSaverAllocInfo ();
XScreenSaverQueryInfo (xdisplay, RootWindow (xdisplay, 0), mit_info);
if (mit_info->idle / 1000 > mit->timeout)
{
guint i = 0;
GtkWidget* view;
KatzeArray* history = katze_object_get_object (mit->browser, "history");
KatzeArray* trash = katze_object_get_object (mit->browser, "trash");
GList* data_items = sokoke_register_privacy_item (NULL, NULL, NULL);
while ((view = midori_browser_get_nth_tab (mit->browser, i++)))
gtk_widget_destroy (view);
midori_browser_set_current_uri (mit->browser, mit->uri);
/* Clear all private data */
if (history != NULL)
katze_array_clear (history);
if (trash != NULL)
katze_array_clear (trash);
for (; data_items != NULL; data_items = g_list_next (data_items))
((SokokePrivacyItem*)(data_items->data))->clear ();
}
}
#else
/* TODO: Implement for other windowing systems */
#endif
return TRUE;
}
static void
midori_setup_inactivity_reset (MidoriBrowser* browser,
gint inactivity_reset,
const gchar* uri)
{
if (inactivity_reset > 0)
{
MidoriInactivityTimeout* mit = g_new (MidoriInactivityTimeout, 1);
mit->browser = browser;
mit->timeout = inactivity_reset;
mit->uri = g_strdup (uri);
g_timeout_add_seconds (inactivity_reset, midori_inactivity_timeout,
mit);
}
}
static void
midori_clear_page_icons_cb (void)
{
gchar* cache = g_build_filename (g_get_user_cache_dir (),
PACKAGE_NAME, "icons", NULL);
sokoke_remove_path (cache, TRUE);
g_free (cache);
cache = g_build_filename (g_get_user_data_dir (),
"webkit", "icondatabase", NULL);
sokoke_remove_path (cache, TRUE);
g_free (cache);
}
static void
midori_clear_web_cookies_cb (void)
{
SoupSession* session = webkit_get_default_session ();
SoupSessionFeature* jar = soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
GSList* cookies = soup_cookie_jar_all_cookies (SOUP_COOKIE_JAR (jar));
SoupSessionFeature* feature;
for (; cookies != NULL; cookies = g_slist_next (cookies))
{
SoupCookie* cookie = cookies->data;
soup_cookie_jar_delete_cookie ((SoupCookieJar*)jar, cookie);
}
soup_cookies_free (cookies);
/* Removing KatzeHttpCookies makes it save outstanding changes */
if ((feature = soup_session_get_feature (session, KATZE_TYPE_HTTP_COOKIES)))
{
g_object_ref (feature);
soup_session_remove_feature (session, feature);
soup_session_add_feature (session, feature);
g_object_unref (feature);
}
}
#ifdef GDK_WINDOWING_X11
static void
midori_clear_flash_cookies_cb (void)
{
gchar* cache = g_build_filename (g_get_home_dir (), ".macromedia",
"Flash_Player", NULL);
sokoke_remove_path (cache, TRUE);
g_free (cache);
}
#endif
static void
midori_clear_saved_logins_cb (void)
{
sqlite3* db;
gchar* path = g_build_filename (sokoke_set_config_dir (NULL), "logins", NULL);
g_unlink (path);
/* Form History database, written by the extension */
katze_assign (path, g_build_filename (sokoke_set_config_dir (NULL),
"extensions", MIDORI_MODULE_PREFIX "formhistory." G_MODULE_SUFFIX, "forms.db", NULL));
if (sqlite3_open (path, &db) == SQLITE_OK)
{
sqlite3_exec (db, "DELETE FROM forms", NULL, NULL, NULL);
sqlite3_close (db);
}
g_free (path);
}
static void
midori_clear_html5_databases_cb (void)
{
webkit_remove_all_web_databases ();
}
#if WEBKIT_CHECK_VERSION (1, 3, 11)
static void
midori_clear_web_cache_cb (void)
{
SoupSession* session = webkit_get_default_session ();
SoupSessionFeature* feature = soup_session_get_feature (session, SOUP_TYPE_CACHE);
gchar* path = g_build_filename (g_get_user_cache_dir (), PACKAGE_NAME, "web", NULL);
soup_cache_clear (SOUP_CACHE (feature));
soup_cache_flush (SOUP_CACHE (feature));
sokoke_remove_path (path, TRUE);
g_free (path);
}
#endif
#if WEBKIT_CHECK_VERSION (1, 3, 13)
static void
midori_clear_offline_appcache_cb (void)
{
/* Changing the size implies clearing the cache */
unsigned long long maximum = webkit_application_cache_get_maximum_size ();
webkit_application_cache_set_maximum_size (maximum - 1);
}
#endif
static void
midori_log_to_file (const gchar* log_domain,
GLogLevelFlags log_level,
const gchar* message,
gpointer user_data)
{
FILE* logfile = fopen ((const char*)user_data, "a");
gchar* level_name = "";
time_t timestamp = time (NULL);
switch (log_level)
{
/* skip irrelevant flags */
case G_LOG_LEVEL_MASK:
case G_LOG_FLAG_FATAL:
case G_LOG_FLAG_RECURSION:
case G_LOG_LEVEL_ERROR:
level_name = "ERROR";
break;
case G_LOG_LEVEL_CRITICAL:
level_name = "CRITICAL";
break;
case G_LOG_LEVEL_WARNING:
level_name = "WARNING";
break;
case G_LOG_LEVEL_MESSAGE:
level_name = "MESSAGE";
break;
case G_LOG_LEVEL_INFO:
level_name = "INFO";
break;
case G_LOG_LEVEL_DEBUG:
level_name = "DEBUG";
break;
}
fprintf (logfile, "%s%s-%s **: %s\n", asctime (localtime (&timestamp)),
log_domain ? log_domain : "Midori", level_name, message);
fclose (logfile);
}
int
main (int argc,
char** argv)
{
gchar* webapp;
gchar* config;
gboolean private;
gboolean diagnostic_dialog;
gboolean back_from_crash;
gboolean run;
gchar* snapshot;
gchar* logfile;
gboolean execute;
gboolean help_execute;
gboolean version;
gchar** uris;
gchar* block_uris;
gint inactivity_reset;
MidoriApp* app;
gboolean result;
GError* error;
GOptionEntry entries[] =
{
{ "app", 'a', 0, G_OPTION_ARG_STRING, &webapp,
N_("Run ADDRESS as a web application"), N_("ADDRESS") },
#if !HAVE_HILDON
{ "config", 'c', 0, G_OPTION_ARG_FILENAME, &config,
N_("Use FOLDER as configuration folder"), N_("FOLDER") },
#endif
{ "private", 'p', 0, G_OPTION_ARG_NONE, &private,
N_("Private browsing, no changes are saved"), NULL },
{ "diagnostic-dialog", 'd', 0, G_OPTION_ARG_NONE, &diagnostic_dialog,
N_("Show a diagnostic dialog"), NULL },
{ "run", 'r', 0, G_OPTION_ARG_NONE, &run,
N_("Run the specified filename as javascript"), NULL },
{ "snapshot", 's', 0, G_OPTION_ARG_STRING, &snapshot,
N_("Take a snapshot of the specified URI"), NULL },
{ "execute", 'e', 0, G_OPTION_ARG_NONE, &execute,
N_("Execute the specified command"), NULL },
{ "help-execute", 0, 0, G_OPTION_ARG_NONE, &help_execute,
N_("List available commands to execute with -e/ --execute"), NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &version,
N_("Display program version"), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &uris,
N_("Addresses"), NULL },
{ "block-uris", 'b', 0, G_OPTION_ARG_STRING, &block_uris,
N_("Block URIs according to regular expression PATTERN"), _("PATTERN") },
#ifdef HAVE_X11_EXTENSIONS_SCRNSAVER_H
{ "inactivity-reset", 'i', 0, G_OPTION_ARG_INT, &inactivity_reset,
/* i18n: CLI: Close tabs, clear private data, open starting page */
N_("Reset Midori after SECONDS seconds of inactivity"), N_("SECONDS") },
#endif
{ "log-file", 'l', 0, G_OPTION_ARG_FILENAME, &logfile,
N_("Redirects console warnings to the specified FILENAME"), N_("FILENAME")},
{ NULL }
};
GString* error_messages;
gchar** extensions;
MidoriWebSettings* settings;
gchar* config_file;
gchar* bookmarks_file;
GKeyFile* speeddial;
gboolean bookmarks_exist;
MidoriStartup load_on_startup;
KatzeArray* search_engines;
KatzeArray* bookmarks;
KatzeArray* history;
KatzeArray* _session;
KatzeArray* trash;
guint i;
gchar* uri;
KatzeItem* item;
gchar* uri_ready;
gchar* errmsg;
sqlite3* db;
gint max_history_age;
gint clear_prefs = MIDORI_CLEAR_NONE;
#ifdef G_ENABLE_DEBUG
gboolean startup_timer = g_getenv ("MIDORI_STARTTIME") != NULL;
#define midori_startup_timer(tmrmsg) if (startup_timer) \
g_debug (tmrmsg, (g_test_timer_last () - g_test_timer_elapsed ()) * -1)
#else
#define midori_startup_timer(tmrmsg)
#endif
#ifdef HAVE_SIGNAL_H
#ifdef SIGHUP
signal (SIGHUP, &signal_handler);
#endif
#ifdef SIGINT
signal (SIGINT, &signal_handler);
#endif
#ifdef SIGTERM
signal (SIGTERM, &signal_handler);
#endif
#ifdef SIGQUIT
signal (SIGQUIT, &signal_handler);
#endif
#endif
midori_app_setup (argv);
/* Parse cli options */
webapp = NULL;
config = NULL;
private = FALSE;
back_from_crash = FALSE;
diagnostic_dialog = FALSE;
run = FALSE;
snapshot = NULL;
logfile = NULL;
execute = FALSE;
help_execute = FALSE;
version = FALSE;
uris = NULL;
block_uris = NULL;
inactivity_reset = 0;
error = NULL;
if (!gtk_init_with_args (&argc, &argv, _("[Addresses]"), entries,
GETTEXT_PACKAGE, &error))
{
g_print ("%s - %s\n", _("Midori"), error->message);
g_error_free (error);
return 1;
}
if (config && !g_path_is_absolute (config))
{
g_critical (_("The specified configuration folder is invalid."));
return 1;
}
/* Private browsing, window title, default config folder */
if (private)
{
if (!config && !webapp)
config = g_build_filename (g_get_user_config_dir (), PACKAGE_NAME, NULL);
/* Mask the timezone, which can be read by Javascript */
g_setenv ("TZ", "UTC", TRUE);
}
else
g_set_application_name (_("Midori"));
if (version)
{
g_print (
"%s %s\n\n"
"Copyright (c) 2007-2011 Christian Dywan\n\n"
"%s\n"
"\t%s\n\n"
"%s\n"
"\thttp://www.twotoasts.de\n",
_("Midori"), PACKAGE_VERSION,
_("Please report comments, suggestions and bugs to:"),
PACKAGE_BUGREPORT,
_("Check for new versions at:")
);
return 0;
}
if (help_execute)
{
MidoriBrowser* browser = midori_browser_new ();
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GList* actions = gtk_action_group_list_actions (action_group);
for (; actions; actions = g_list_next (actions))
{
GtkAction* action = actions->data;
const gchar* name = gtk_action_get_name (action);
const gchar* space = " ";
gchar* padding = g_strndup (space, strlen (space) - strlen (name));
gchar* label = katze_object_get_string (action, "label");
gchar* stripped = katze_strip_mnemonics (label);
gchar* tooltip = katze_object_get_string (action, "tooltip");
g_print ("%s%s%s%s%s\n", name, padding, stripped,
tooltip ? ": " : "", tooltip ? tooltip : "");
g_free (tooltip);
g_free (padding);
g_free (label);
g_free (stripped);
}
g_list_free (actions);
gtk_widget_destroy (GTK_WIDGET (browser));
return 0;
}
if (snapshot)
{
gchar* filename;
gint fd;
GtkWidget* web_view;
#if HAVE_OFFSCREEN
GtkWidget* offscreen;
GdkScreen* screen;
fd = g_file_open_tmp ("snapshot-XXXXXX.png", &filename, &error);
#else
fd = g_file_open_tmp ("snapshot-XXXXXX.pdf", &filename, &error);
#endif
close (fd);
error = NULL;
if (error)
{
g_error ("%s", error->message);
return 1;
}
if (g_unlink (filename) == -1)
{
g_error ("%s", g_strerror (errno));
return 1;
}
web_view = webkit_web_view_new ();
#if HAVE_OFFSCREEN
offscreen = gtk_offscreen_window_new ();
gtk_container_add (GTK_CONTAINER (offscreen), web_view);
if ((screen = gdk_screen_get_default ()))
gtk_widget_set_size_request (web_view,
gdk_screen_get_width (screen), gdk_screen_get_height (screen));
else
gtk_widget_set_size_request (web_view, 800, 600);
gtk_widget_show_all (offscreen);
#endif
g_signal_connect (web_view, "load-finished",
G_CALLBACK (snapshot_load_finished_cb), filename);
webkit_web_view_open (WEBKIT_WEB_VIEW (web_view), snapshot);
gtk_main ();
g_free (filename);
return 0;
}
if (logfile)
{
g_log_set_default_handler (midori_log_to_file, (gpointer)logfile);
}
sokoke_register_privacy_item ("page-icons", _("Website icons"),
G_CALLBACK (midori_clear_page_icons_cb));
/* i18n: Logins and passwords in websites and web forms */
sokoke_register_privacy_item ("formhistory", _("Saved logins and _passwords"),
G_CALLBACK (midori_clear_saved_logins_cb));
sokoke_register_privacy_item ("web-cookies", _("Cookies"),
G_CALLBACK (midori_clear_web_cookies_cb));
#ifdef GDK_WINDOWING_X11
sokoke_register_privacy_item ("flash-cookies", _("'Flash' Cookies"),
G_CALLBACK (midori_clear_flash_cookies_cb));
#endif
sokoke_register_privacy_item ("html5-databases", _("HTML5 _Databases"),
G_CALLBACK (midori_clear_html5_databases_cb));
#if WEBKIT_CHECK_VERSION (1, 3, 11)
sokoke_register_privacy_item ("web-cache", _("Web Cache"),
G_CALLBACK (midori_clear_web_cache_cb));
sokoke_register_privacy_item ("offline-appcache", _("Offline Application Cache"),
G_CALLBACK (midori_clear_offline_appcache_cb));
#endif
/* Web Application or Private Browsing support */
if (webapp || private || run)
{
SoupSession* session = webkit_get_default_session ();
MidoriBrowser* browser = midori_browser_new ();
/* Update window icon according to page */
g_signal_connect (browser, "notify::load-status",
G_CALLBACK (midori_web_app_browser_notify_load_status_cb), NULL);
g_signal_connect (browser, "new-window",
G_CALLBACK (midori_web_app_browser_new_window_cb), NULL);
g_object_set_data (G_OBJECT (webkit_get_default_session ()),
"pass-through-console", (void*)1);
midori_startup_timer ("Browser: \t%f");
if (config)
{
settings = settings_and_accels_new (config, &extensions);
g_strfreev (extensions);
search_engines = search_engines_new_from_folder (config, NULL);
g_object_set (browser, "search-engines", search_engines, NULL);
g_object_unref (search_engines);
speeddial = speeddial_new_from_file (config, &error);
g_object_set (browser, "speed-dial", speeddial, NULL);
}
else
settings = g_object_ref (midori_browser_get_settings (browser));
if (private)
{
/* In-memory trash for re-opening closed tabs */
trash = katze_array_new (KATZE_TYPE_ITEM);
g_signal_connect_after (trash, "add-item",
G_CALLBACK (midori_trash_add_item_no_save_cb), NULL);
g_object_set (browser, "trash", trash, NULL);
g_object_set (settings,
"preferred-languages", "en",
"enable-private-browsing", TRUE,
#ifdef HAVE_LIBSOUP_2_29_91
"first-party-cookies-only", TRUE,
#endif
"enable-html5-database", FALSE,
"enable-html5-local-storage", FALSE,
"enable-offline-web-application-cache", FALSE,
/* Arguably DNS prefetching is or isn't a privacy concern. For the
* lack of more fine-grained control we'll go the safe route. */
#if WEBKIT_CHECK_VERSION (1, 3, 11)
"enable-dns-prefetching", FALSE,
#endif
"strip-referer", TRUE, NULL);
midori_browser_set_action_visible (browser, "Tools", FALSE);
midori_browser_set_action_visible (browser, "ClearPrivateData", FALSE);
}
if (private || !config)
{
/* Disable saving by setting an unwritable folder */
sokoke_set_config_dir ("/");
}
midori_load_soup_session (settings);
if (block_uris)
g_signal_connect (session, "request-queued",
G_CALLBACK (midori_soup_session_block_uris_cb),
g_strdup (block_uris));
if (run)
{
gchar* script = NULL;
error = NULL;
if (g_file_get_contents (uris ? *uris : NULL, &script, NULL, &error))
{
#if 0 /* HAVE_OFFSCREEN */
GtkWidget* offscreen = gtk_offscreen_window_new ();
#endif
gchar* msg = NULL;
GtkWidget* view = midori_view_new_with_item (NULL, settings);
g_object_set (settings, "open-new-pages-in", MIDORI_NEW_PAGE_WINDOW, NULL);
midori_browser_add_tab (browser, view);
#if 0 /* HAVE_OFFSCREEN */
gtk_container_add (GTK_CONTAINER (offscreen), GTK_WIDGET (browser));
gtk_widget_show_all (offscreen);
#else
gtk_widget_show_all (GTK_WIDGET (browser));
gtk_widget_hide (GTK_WIDGET (browser));
#endif
midori_view_execute_script (MIDORI_VIEW (view), script, &msg);
if (msg != NULL)
{
g_error ("%s\n", msg);
g_free (msg);
}
}
else if (error != NULL)
{
g_error ("%s\n", error->message);
g_error_free (error);
}
else
g_error ("%s\n", _("An unknown error occured"));
g_free (script);
}
if (webapp)
{
gchar* tmp_uri = midori_prepare_uri (webapp);
midori_browser_set_action_visible (browser, "Menubar", FALSE);
midori_browser_set_action_visible (browser, "CompactMenu", FALSE);
midori_browser_add_uri (browser, tmp_uri ? tmp_uri : webapp);
g_object_set (settings, "homepage", tmp_uri, NULL);
g_free (tmp_uri);
g_object_set (settings,
"show-menubar", FALSE,
"show-navigationbar", FALSE,
"toolbar-items", "Back,Forward,ReloadStop,Location,Homepage",
"show-statusbar", FALSE,
"enable-developer-extras", FALSE,
NULL);
}
g_object_set (settings, "show-panel", FALSE,
"last-window-state", MIDORI_WINDOW_NORMAL,
NULL);
midori_browser_set_action_visible (browser, "Panel", FALSE);
g_object_set (browser, "settings", settings, NULL);
midori_startup_timer ("Setup config: \t%f");
g_object_unref (settings);
g_signal_connect (browser, "quit",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (browser, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
if (!run)
{
gtk_widget_show (GTK_WIDGET (browser));
midori_browser_activate_action (browser, "Location");
}
if (execute)
{
for (i = 0; uris[i] != NULL; i++)
midori_browser_activate_action (browser, uris[i]);
}
else if (uris != NULL)
{
for (i = 0; uris[i] != NULL; i++)
{
gchar* new_uri = midori_prepare_uri (uris[i]);
midori_browser_add_uri (browser, new_uri);
g_free (new_uri);
}
}
if (midori_browser_get_current_uri (browser) == NULL)
midori_browser_add_uri (browser, "about:blank");
midori_setup_inactivity_reset (browser, inactivity_reset, webapp);
midori_startup_timer ("App created: \t%f");
gtk_main ();
return 0;
}
/* FIXME: Inactivity reset is only supported for app mode */
if (inactivity_reset > 0)
g_error ("--inactivity-reset is currently only supported with --app.");
sokoke_set_config_dir (config);
app = midori_app_new ();
katze_assign (config, (gchar*)sokoke_set_config_dir (NULL));
midori_startup_timer ("App created: \t%f");
/* FIXME: The app might be 'running' but actually showing a dialog
after a crash, so running a new window isn't a good idea. */
if (midori_app_instance_is_running (app))
{
GtkWidget* dialog;
if (execute)
result = midori_app_send_command (app, uris);
else if (uris)
{
/* Encode any IDN addresses because libUnique doesn't like them */
i = 0;
while (uris[i] != NULL)
{
gchar* new_uri = midori_prepare_uri (uris[i]);
gchar* escaped_uri = g_uri_escape_string (
new_uri ? new_uri : uris[i], NULL, FALSE);
g_free (new_uri);
katze_assign (uris[i], escaped_uri);
i++;
}
result = midori_app_instance_send_uris (app, uris);
}
else
result = midori_app_instance_send_new_browser (app);
if (result)
return 0;
dialog = gtk_message_dialog_new (NULL,
0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s",
_("An instance of Midori is already running but not responding.\n"));
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_DELETE_EVENT)
gtk_widget_destroy (dialog);
/* FIXME: Allow killing the existing instance */
return 1;
}
katze_mkdir_with_parents (config, 0700);
/* Load configuration file */
error_messages = g_string_new (NULL);
error = NULL;
settings = settings_and_accels_new (config, &extensions);
g_object_set (settings, "enable-developer-extras", TRUE, NULL);
g_object_set (settings, "enable-html5-database", TRUE, NULL);
midori_startup_timer ("Config and accels read: \t%f");
/* Load search engines */
search_engines = search_engines_new_from_folder (config, error_messages);
/* Pick first search engine as default if not set */
g_object_get (settings, "location-entry-search", &uri, NULL);
if (!(uri && *uri) && !katze_array_is_empty (search_engines))
{
item = katze_array_get_nth_item (search_engines, 0);
g_object_set (settings, "location-entry-search",
katze_item_get_uri (item), NULL);
}
g_free (uri);
midori_startup_timer ("Search read: \t%f");
bookmarks = katze_array_new (KATZE_TYPE_ARRAY);
bookmarks_file = g_build_filename (config, "bookmarks.db", NULL);
bookmarks_exist = g_access (bookmarks_file, F_OK) == 0;
errmsg = NULL;
if ((db = midori_bookmarks_initialize (bookmarks, bookmarks_file, &errmsg)) == NULL)
{
g_string_append_printf (error_messages,
_("Bookmarks couldn't be loaded: %s\n"), errmsg);
errmsg = NULL;
}
else if (!bookmarks_exist)
{
/* Initial creation, import old bookmarks */
gchar* old_bookmarks;
if (g_path_is_absolute (BOOKMARK_FILE))
old_bookmarks = g_strdup (BOOKMARK_FILE);
else
old_bookmarks = g_build_filename (config, BOOKMARK_FILE, NULL);
if (g_access (old_bookmarks, F_OK) == 0)
{
midori_bookmarks_import (old_bookmarks, db);
/* Leave old bookmarks around */
}
g_free (old_bookmarks);
}
g_object_set_data (G_OBJECT (bookmarks), "db", db);
midori_startup_timer ("Bookmarks read: \t%f");
config_file = NULL;
_session = katze_array_new (KATZE_TYPE_ITEM);
load_on_startup = katze_object_get_enum (settings, "load-on-startup");
#if HAVE_LIBXML
if (load_on_startup >= MIDORI_STARTUP_LAST_OPEN_PAGES)
{
katze_assign (config_file, build_config_filename ("session.xbel"));
error = NULL;
if (!midori_array_from_file (_session, config_file, "xbel", &error))
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The session couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
}
#endif
midori_startup_timer ("Session read: \t%f");
trash = katze_array_new (KATZE_TYPE_ITEM);
#if HAVE_LIBXML
katze_assign (config_file, g_build_filename (config, "tabtrash.xbel", NULL));
error = NULL;
if (!midori_array_from_file (trash, config_file, "xbel", &error))
{
if (error->code != G_FILE_ERROR_NOENT)
g_string_append_printf (error_messages,
_("The trash couldn't be loaded: %s\n"), error->message);
g_error_free (error);
}
#endif
midori_startup_timer ("Trash read: \t%f");
history = katze_array_new (KATZE_TYPE_ARRAY);
katze_assign (config_file, g_build_filename (config, "history.db", NULL));
errmsg = NULL;
if (!midori_history_initialize (history, config_file, bookmarks_file, &errmsg))
{
g_string_append_printf (error_messages,
_("The history couldn't be loaded: %s\n"), errmsg);
errmsg = NULL;
}
g_free (bookmarks_file);
midori_startup_timer ("History read: \t%f");
error = NULL;
speeddial = speeddial_new_from_file (config, &error);
/* In case of errors */
if (error_messages->len)
{
GdkScreen* screen;
GtkIconTheme* icon_theme;
GtkWidget* dialog = gtk_message_dialog_new (
NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE,
_("The following errors occured:"));
gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE);
gtk_window_set_title (GTK_WINDOW (dialog), g_get_application_name ());
screen = gtk_widget_get_screen (dialog);
if (screen)
{
icon_theme = gtk_icon_theme_get_for_screen (screen);
if (gtk_icon_theme_has_icon (icon_theme, "midori"))
gtk_window_set_icon_name (GTK_WINDOW (dialog), "midori");
else
gtk_window_set_icon_name (GTK_WINDOW (dialog), "web-browser");
}
gtk_message_dialog_format_secondary_text (
GTK_MESSAGE_DIALOG (dialog), "%s", error_messages->str);
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
_("_Ignore"), GTK_RESPONSE_ACCEPT,
NULL);
if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
{
g_object_unref (settings);
g_object_unref (search_engines);
g_object_unref (bookmarks);
g_object_unref (_session);
g_object_unref (trash);
g_object_unref (history);
g_string_free (error_messages, TRUE);
return 0;
}
gtk_widget_destroy (dialog);
/* FIXME: Since we will overwrite files that could not be loaded
, would we want to make backups? */
}
g_string_free (error_messages, TRUE);
/* If -e or --execute was specified, "uris" refers to the command. */
if (!execute)
{
/* Open as many tabs as we have uris, seperated by pipes */
i = 0;
while (uris && uris[i])
{
uri = strtok (g_strdup (uris[i]), "|");
while (uri != NULL)
{
item = katze_item_new ();
uri_ready = midori_prepare_uri (uri);
katze_item_set_uri (item, uri_ready ? uri_ready : uri);
g_free (uri_ready);
/* Never delay command line arguments */
katze_item_set_meta_integer (item, "delay", 0);
katze_array_add_item (_session, item);
uri = strtok (NULL, "|");
}
g_free (uri);
i++;
}
}
if (1)
{
g_signal_connect_after (search_engines, "add-item",
G_CALLBACK (midori_search_engines_modify_cb), search_engines);
g_signal_connect_after (search_engines, "remove-item",
G_CALLBACK (midori_search_engines_modify_cb), search_engines);
if (!katze_array_is_empty (search_engines))
{
KATZE_ARRAY_FOREACH_ITEM (item, search_engines)
g_signal_connect_after (item, "notify",
G_CALLBACK (midori_search_engines_modify_cb), search_engines);
g_signal_connect_after (search_engines, "move-item",
G_CALLBACK (midori_search_engines_move_item_cb), search_engines);
}
}
g_signal_connect_after (trash, "add-item",
G_CALLBACK (midori_trash_add_item_cb), NULL);
g_signal_connect_after (trash, "remove-item",
G_CALLBACK (midori_trash_remove_item_cb), NULL);
katze_item_set_parent (KATZE_ITEM (_session), app);
g_object_set_data (G_OBJECT (app), "extensions", extensions);
/* We test for the presence of a dummy file which is created once
and deleted during normal runtime, but persists in case of a crash. */
katze_assign (config_file, g_build_filename (config, "running", NULL));
if (g_access (config_file, F_OK) == 0)
back_from_crash = TRUE;
else
g_file_set_contents (config_file, "RUNNING", -1, NULL);
if (back_from_crash
&& katze_object_get_boolean (settings, "show-crash-dialog")
&& !katze_array_is_empty (_session))
diagnostic_dialog = TRUE;
if (diagnostic_dialog)
{
load_on_startup = midori_show_diagnostic_dialog (settings, _session);
if (load_on_startup == G_MAXINT)
return 0;
}
g_object_set_data (G_OBJECT (settings), "load-on-startup", GINT_TO_POINTER (load_on_startup));
midori_startup_timer ("Signal setup: \t%f");
g_object_set (app, "settings", settings,
"bookmarks", bookmarks,
"trash", trash,
"search-engines", search_engines,
"history", history,
"speed-dial", speeddial,
NULL);
g_object_unref (history);
g_object_unref (search_engines);
g_object_unref (bookmarks);
g_object_unref (trash);
g_object_unref (settings);
g_signal_connect (app, "add-browser",
G_CALLBACK (midori_app_add_browser_cb), NULL);
midori_startup_timer ("App prepared: \t%f");
g_idle_add (midori_load_soup_session_full, settings);
g_idle_add (midori_load_extensions, app);
g_idle_add (midori_load_session, _session);
if (execute)
g_object_set_data (G_OBJECT (app), "execute-command", uris);
if (block_uris)
g_signal_connect (webkit_get_default_session (), "request-queued",
G_CALLBACK (midori_soup_session_block_uris_cb),
g_strdup (block_uris));
gtk_main ();
settings = katze_object_get_object (app, "settings");
settings_notify_cb (settings, NULL, app);
g_object_get (settings, "maximum-history-age", &max_history_age, NULL);
midori_history_terminate (history, max_history_age);
/* Clear data on quit, according to the Clear private data dialog */
g_object_get (settings, "clear-private-data", &clear_prefs, NULL);
if (clear_prefs & MIDORI_CLEAR_ON_QUIT)
{
GList* data_items = sokoke_register_privacy_item (NULL, NULL, NULL);
gchar* clear_data = katze_object_get_string (settings, "clear-data");
midori_remove_config_file (clear_prefs, MIDORI_CLEAR_SESSION, "session.xbel");
midori_remove_config_file (clear_prefs, MIDORI_CLEAR_HISTORY, "history.db");
midori_remove_config_file (clear_prefs, MIDORI_CLEAR_HISTORY, "tabtrash.xbel");
for (; data_items != NULL; data_items = g_list_next (data_items))
{
SokokePrivacyItem* privacy = data_items->data;
if (clear_data && strstr (clear_data, privacy->name))
privacy->clear ();
}
g_free (clear_data);
}
/* Removing KatzeHttpCookies makes it save outstanding changes */
soup_session_remove_feature_by_type (webkit_get_default_session (),
KATZE_TYPE_HTTP_COOKIES);
load_on_startup = katze_object_get_int (settings, "load-on-startup");
if (load_on_startup < MIDORI_STARTUP_LAST_OPEN_PAGES)
{
katze_assign (config_file, g_build_filename (config, "session.xbel", NULL));
g_unlink (config_file);
}
g_object_unref (settings);
g_key_file_free (speeddial);
g_object_unref (app);
g_free (config_file);
return 0;
}