Merge branch 'upstream-unstable'

This commit is contained in:
Yves-Alexis Perez 2012-03-08 23:42:46 +01:00
commit bf2d83aba2
78 changed files with 25888 additions and 20487 deletions

View file

@ -1,5 +1,31 @@
This file is licensed under the terms of the expat license, see the file EXPAT.
v0.4.4:
+ Disable page cache with < 352 MB RAM
+ Display filename in download dialog
+ Fix box packing in GTK+3 (in most cases)
+ Enable experimental HTML5 fullscreen API
+ Harden IPv6 address recognition in location
+ Experimental site data policy support (see FAQ)
+ Close tabs by middle clicking close button
+ Merge cookies and other data in Clear Private Data
+ Improve KatzeArrayAction for Unity menuproxy compatibility
+ Use GDateTime for history to avoid broken C runtimes
+ Add Midori tag to DuckDuckGo default URI
+ Rewrite completion popup resizing
+ Streamline page icon loading stages and fallbacks
+ Disable clipboard work-around for WebKit >= 1.4.3
+ Re-word .desktop entry as an action
+ Display informative text in private browsing
+ Consistent clear icons in entries
+ Revised download filename generation
+ Add 'Open in Image Viewer' menu item
+ Formhistory 2.0 with GDOM support
+ Handle javascript: and mailto: links better
+ Handle = key in Ukrainian layout better
+ Fix bookmark export and deletion of bookmark folders
+ Speed dial shortcut re-reordering by DND
v0.4.3:
+ Implement about:widgets to test rendering
+ Fix resizing of inspector by applying a minimum size

View file

@ -67,6 +67,8 @@ If you want to "dry run" without WebKitGTK+ rendering, try this:
'MIDORI_UNARMED=1 _build_/default/midori/midori'
To disable Netscape plugins, use MOZ_PLUGIN_PATH=/.
To debug extensions you can specify the path:
'export MIDORI_EXTENSION_PATH=_build_/default/extensions'

57
data/about.css Normal file
View file

@ -0,0 +1,57 @@
/*
about: page style template for Midori.
This file is licensed under the terms of the expat license, see the file EXPAT.
*/
body {
background-color: #eee;
margin: 0;
padding: 0;
}
#container {
background: #f6fff3;
min-width: 70%;
max-width: 70%;
margin: 2em auto 1em;
padding: 1em;
border: 0.2em solid #9acb7f;
-webkit-border-radius: 1em;
}
icon {
float: left;
padding-left: 1%;
padding-top: 1%;
}
#main {
float: right;
width: 90%;
}
h1 {
font-size: 1.4em;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#logo {
position: absolute; right: 15px; bottom: 15px;
z-index: -1;
}
button span,
button img {
vertical-align: middle;
padding: 2px 1px;
}
message {
font-size: 1.1em;
}
description {
font-size: 1em;
}

View file

@ -2,67 +2,11 @@
Error page template for Midori.
This file is licensed under the terms of the expat license, see the file EXPAT.
-->
<html>
<head>
<title>{title}</title>
<link rel="shortcut icon" href="{icon}" />
<style type="text/css">
body {
background-color: #eee;
margin: 0;
padding: 0;
}
#container {
background: #f6fff3;
min-width: 70%;
max-width: 70%;
margin: 2em auto 1em;
padding: 1em;
border: 0.2em solid #9acb7f;
-webkit-border-radius: 1em;
}
icon {
float: left;
padding-left: 1%;
padding-top: 1%;
}
#main {
float: right;
width: 90%;
}
h1 {
font-size: 1.4em;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#logo {
position: absolute; right: 15px; bottom: 15px;
z-index: -1;
}
button span,
button img {
vertical-align: middle;
padding: 2px 1px;
}
message {
font-size: 1.1em;
}
description {
font-size: 1em;
}
</style>
<link rel="stylesheet" type="text/css" href="res://about.css" />
</head>
<body>
<div id="container">

View file

@ -3,7 +3,7 @@ Version=1.0
Type=Application
_Name=Midori
_GenericName=Web Browser
_Comment=Lightweight web browser
_Comment=Browse the Web
_X-GNOME-Keywords=Internet;WWW;Explorer
_X-AppInstall-Keywords=Internet;WWW;Explorer
Categories=GTK;Network;WebBrowser;

View file

@ -55,10 +55,7 @@
width: 85%;
height: 75%;
margin: auto;
-webkit-box-shadow: 0 4px 18px rgba(0,0,0,.3), 0 0 2px #fff inset;
background-image: -webkit-gradient(
linear, center top, center bottom,
from(#f6f6f6), to(#e3e3e3));
-webkit-box-shadow: 0 2px 5px rgba(0,0,0,.3), 0 0 0px #fff inset;
border: 1px solid #bcbcbc;
border-bottom-color: #a0a0a0;
position: relative;
@ -75,9 +72,15 @@
div.shortcut .preview.new .add {
display: block;
height: 100%;
width: 50%;
width: 100%;
margin: 0 auto;
cursor: pointer;
-webkit-box-shadow: 0 2px 5px rgba(0,0,0,.3), 0 0 0px #fff inset;
background-image: -webkit-gradient(
linear, center top, center bottom,
from(#f6f6f6), to(#e3e3e3));
background-repeat: repeat-x;
-webkit-border-radius: 3px;
}
.title {
@ -126,6 +129,11 @@
-webkit-border-bottom-left-radius: 10px;
visibility: hidden;
}
.selected {
outline: 1px dotted black;
background-color: #eef;
}
</style>
<script type="text/javascript">
@ -196,6 +204,72 @@
return false;
}
var firstNode, secondNode;
var cursor;
var dial = document.getElementsByClassName("shortcut");
var get_dial_div = function (ele) {
var dial_div;
if (ele.nodeName == 'IMG')
dial_div = ele.parentNode.parentNode.parentNode;
if (ele.className == 'title')
dial_div = ele.parentNode;
if (ele.className.indexOf ('shortcut') != -1)
dial_dir = ele;
return dial_div;
}
var click = function (ev) {
ev.preventDefault();
var ele = ev.target;
cursor = ele.style.cursor;
ele.style.cursor = 'move';
var eparent = get_dial_div (ele);
if (eparent != undefined) {
eparent.className = 'shortcut selected';
firstNode = eparent.id;
}
document.RemoveEventListener('click', click, false);
};
var up = function (ev) {
ele = ev.target;
var eparent = get_dial_div (ele);
ele.style.cursor = cursor;
secondNode = eparent.id;
/* ommit just mere clicking the dial */
if (firstNode != secondNode && firstNode != undefined)
swap();
};
var over = function (ev) {
if (ev == undefined)
return;
var ele = ev.target;
var eparent = get_dial_div (ele);
if (firstNode != undefined)
{
eparent.className = 'shortcut selected';
for (var i = 0; i <= dial.length; i++) {
if (eparent.id != firstNode.id && dial[i].id != eparent.id) {
dial[i].className = 'shortcut';
}
}
}
ele.style.cursor = cursor;
}
function swap () {
console.log ("speed_dial-save-swap " + firstNode + " " + secondNode);
};
document.addEventListener('mousedown', click, false);
document.addEventListener('mouseup', up, false);
document.addEventListener('mouseover', over, false);
</script>
</head>
<body>

View file

@ -1,6 +1,6 @@
/*
Copyright (C) 2009-2010 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de>
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -9,7 +9,6 @@
See the file COPYING for the full license text.
*/
#include <midori/midori.h>
#include <glib/gstdio.h>
@ -35,13 +34,12 @@
#define adblock_debug(dmsg, darg1, darg2) /* nothing */
#endif
static GHashTable* pattern;
static GHashTable* keys;
static GHashTable* optslist;
static GHashTable* urlcache;
static GString* blockcss;
static GString* blockcssprivate;
static gchar* blockscript = NULL;
static GHashTable* pattern = NULL;
static GHashTable* keys = NULL;
static GHashTable* optslist = NULL;
static GHashTable* urlcache = NULL;
static GHashTable* blockcssprivate = NULL;
static GString* blockcss = NULL;
#ifdef G_ENABLE_DEBUG
static guint debug;
#endif
@ -54,43 +52,83 @@ adblock_reload_rules (MidoriExtension* extension,
gboolean custom_only);
static gchar*
adblock_build_js (const gchar* private)
adblock_build_js (const gchar* uri)
{
return g_strdup_printf (
gchar* domain;
const gchar* style;
GString* subdomain;
GString* code;
int cnt = 0, blockscnt = 0;
gchar** subdomains;
domain = midori_uri_parse_hostname (uri, NULL);
subdomains = g_strsplit (domain, ".", -1);
g_free (domain);
if (!subdomains)
return NULL;
code = g_string_new (
"window.addEventListener ('DOMContentLoaded',"
"function () {"
" if (document.getElementById('madblock'))"
" return;"
// Get just domain name from URL
" var URL = location.href.match(/:\\/\\/(.[^/]+)/)[1];"
" var sites = new Array(); %s;"
" var public = '.madblockplaceholder ';"
// Split domain into subdomain parts
" var subdomains = URL.split ('.');"
" var hostname = subdomains [subdomains.length - 1];"
" var i = subdomains.length - 2;"
// Check if any of subdomains do have blocking rules
" while (i >= 0) {"
" hostname = subdomains [i] + '.' + hostname;"
" if (sites [hostname])"
" public += ', ' + sites [hostname];"
" i--;"
" }"
" public += ' {display: none !important}';"
" public = '");
cnt = g_strv_length (subdomains) - 1;
subdomain = g_string_new (subdomains [cnt]);
g_string_prepend_c (subdomain, '.');
cnt--;
while (cnt >= 0)
{
g_string_prepend (subdomain, subdomains[cnt]);
if ((style = g_hash_table_lookup (blockcssprivate, subdomain->str)))
{
g_string_append (code, style);
g_string_append_c (code, ',');
blockscnt++;
}
g_string_prepend_c (subdomain, '.');
cnt--;
}
g_string_free (subdomain, TRUE);
g_strfreev (subdomains);
if (blockscnt == 0)
return g_string_free (code, TRUE);
g_string_append (code,
" zz-non-existent {display: none !important}';"
" var mystyle = document.createElement('style');"
" mystyle.setAttribute('type', 'text/css');"
" mystyle.setAttribute('id', 'madblock');"
" mystyle.appendChild(document.createTextNode(public));"
" var head = document.getElementsByTagName('head')[0];"
" if (head) head.appendChild(mystyle);"
"}, true);",
private);
"}, true);");
return g_string_free (code, FALSE);
}
static GString*
adblock_fixup_regexp (const gchar* prefix,
gchar* src);
static void
adblock_destroy_db ()
{
if (blockcss)
g_string_free (blockcss, TRUE);
blockcss = NULL;
g_hash_table_destroy (pattern);
pattern = NULL;
g_hash_table_destroy (optslist);
optslist = NULL;
g_hash_table_destroy (urlcache);
urlcache = NULL;
g_hash_table_destroy (blockcssprivate);
blockcssprivate = NULL;
}
static void
adblock_init_db ()
{
@ -106,12 +144,13 @@ adblock_init_db ()
urlcache = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
(GDestroyNotify)g_free);
blockcssprivate = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
(GDestroyNotify)g_free);
if (blockcss && blockcss->len > 0)
g_string_free (blockcss, TRUE);
if (blockcssprivate && blockcssprivate->len > 0)
g_string_free (blockcssprivate, TRUE);
blockcss = g_string_new ("z-non-exist");
blockcssprivate = g_string_new ("");
}
static void
@ -119,23 +158,9 @@ adblock_download_notify_status_cb (WebKitDownload* download,
GParamSpec* pspec,
MidoriExtension* extension)
{
gchar* path;
MidoriApp* app;
MidoriWebSettings* settings;
if (webkit_download_get_status (download) != WEBKIT_DOWNLOAD_STATUS_FINISHED)
return;
path = g_filename_from_uri (webkit_download_get_destination_uri (download), NULL, NULL);
adblock_parse_file (path);
g_free (path);
app = midori_extension_get_app (extension);
settings = katze_object_get_object (app, "settings");
g_string_append (blockcss, " {display: none !important}\n");
midori_web_settings_add_style (settings, "adblock-blockcss", blockcss->str);
katze_assign (blockscript, adblock_build_js (blockcssprivate->str));
g_object_unref (settings);
adblock_reload_rules (extension, FALSE);
}
static gchar*
@ -174,6 +199,8 @@ adblock_reload_rules (MidoriExtension* extension,
MidoriApp* app = midori_extension_get_app (extension);
MidoriWebSettings* settings = katze_object_get_object (app, "settings");
if (pattern)
adblock_destroy_db ();
adblock_init_db ();
custom_list = g_build_filename (midori_extension_get_config_dir (extension),
@ -215,7 +242,6 @@ adblock_reload_rules (MidoriExtension* extension,
g_strfreev (filters);
g_string_append (blockcss, " {display: none !important}\n");
katze_assign (blockscript, adblock_build_js (blockcssprivate->str));
midori_web_settings_add_style (settings, "adblock-blockcss", blockcss->str);
g_object_unref (settings);
}
@ -622,7 +648,9 @@ adblock_check_rule (GRegex* regex,
return FALSE;
}
/* TODO: Domain opt check */
#ifdef G_ENABLE_DEBUG
adblock_debug ("blocked by pattern regexp=%s -- %s", g_regex_get_pattern (regex), req_uri);
#endif
return TRUE;
}
@ -769,11 +797,6 @@ adblock_resource_request_starting_cb (WebKitWebView* web_view,
if (midori_uri_is_blank (page_uri))
return;
/* Never filter the main page itself */
if (web_frame == webkit_web_view_get_main_frame (web_view)
&& webkit_web_frame_get_load_status (web_frame) == WEBKIT_LOAD_PROVISIONAL)
return;
req_uri = webkit_network_request_get_uri (request);
if (!midori_uri_is_http (req_uri)
|| g_str_has_suffix (req_uri, "favicon.ico"))
@ -932,13 +955,19 @@ adblock_window_object_cleared_cb (WebKitWebView* web_view,
JSObjectRef js_window)
{
const char *page_uri;
gchar* script;
page_uri = webkit_web_frame_get_uri (web_frame);
/* Don't add adblock css into speeddial and about: pages */
if (!midori_uri_is_http (page_uri))
return;
g_free (sokoke_js_script_eval (js_context, blockscript, NULL));
script = adblock_build_js (page_uri);
if (!script)
return;
g_free (sokoke_js_script_eval (js_context, script, NULL));
g_free (script);
}
static void
@ -1089,7 +1118,9 @@ adblock_compile_regexp (GString* gpatt,
if (!g_regex_match_simple ("[\\*]", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) &&
!g_hash_table_lookup (keys, sig))
{
#ifdef G_ENABLE_DEBUG
adblock_debug ("sig: %s %s", sig, patt);
#endif
g_hash_table_insert (keys, sig, regex);
g_hash_table_insert (optslist, sig, g_strdup (opts));
signature_count++;
@ -1099,7 +1130,9 @@ adblock_compile_regexp (GString* gpatt,
if (g_regex_match_simple ("^\\*", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) &&
!g_hash_table_lookup (pattern, patt))
{
#ifdef G_ENABLE_DEBUG
adblock_debug ("patt2: %s %s", sig, patt);
#endif
g_hash_table_insert (pattern, patt, regex);
g_hash_table_insert (optslist, patt, g_strdup (opts));
}
@ -1115,7 +1148,9 @@ adblock_compile_regexp (GString* gpatt,
}
else
{
#ifdef G_ENABLE_DEBUG
adblock_debug ("patt: %s%s", patt, "");
#endif
/* Pattern is a regexp chars */
g_hash_table_insert (pattern, patt, regex);
g_hash_table_insert (optslist, patt, g_strdup (opts));
@ -1170,7 +1205,9 @@ adblock_add_url_pattern (gchar* prefix,
format_patt = adblock_fixup_regexp (prefix, patt);
#ifdef G_ENABLE_DEBUG
adblock_debug ("got: %s opts %s", format_patt->str, opts);
#endif
should_free = adblock_compile_regexp (format_patt, opts);
if (data[1] && data[2])
@ -1200,6 +1237,22 @@ adblock_frame_add (gchar* line)
g_string_append (blockcss, line);
}
static inline void
adblock_update_css_hash (gchar* domain,
gchar* value)
{
const gchar* olddata;
gchar* newdata;
if ((olddata = g_hash_table_lookup (blockcssprivate, domain)))
{
newdata = g_strconcat (olddata, " , ", value, NULL);
g_hash_table_replace (blockcssprivate, g_strdup (domain), newdata);
}
else
g_hash_table_insert (blockcssprivate, g_strdup (domain), g_strdup (value));
}
static inline void
adblock_frame_add_private (const gchar* line,
const gchar* sep)
@ -1234,15 +1287,13 @@ adblock_frame_add_private (const gchar* line,
/* strip ~ from domain */
if (domain[0] == '~')
domain++;
g_string_append_printf (blockcssprivate, ";sites['%s']+=',%s'",
g_strstrip (domain), data[1]);
adblock_update_css_hash (g_strstrip (domain), data[1]);
}
g_strfreev (domains);
}
else
{
g_string_append_printf (blockcssprivate, ";sites['%s']+=',%s'",
data[0], data[1]);
adblock_update_css_hash (data[0], data[1]);
}
g_strfreev (data);
}
@ -1250,12 +1301,10 @@ adblock_frame_add_private (const gchar* line,
static gchar*
adblock_parse_line (gchar* line)
{
if (!line)
return NULL;
g_strchomp (line);
/* Ignore comments and new lines */
if (line[0] == '!')
/* Skip invalid, empty and comment lines */
if (!(line && line[0] != ' ' && line[0] != '!' && line[0]))
return NULL;
/* FIXME: No support for whitelisting */
if (line[0] == '@' && line[1] == '@')
return NULL;
@ -1263,9 +1312,7 @@ adblock_parse_line (gchar* line)
if (line[0] == '[')
return NULL;
/* Skip garbage */
if (line[0] == ' ' || !line[0])
return NULL;
g_strchomp (line);
/* Got CSS block hider */
if (line[0] == '#' && line[1] == '#' )
@ -1283,13 +1330,13 @@ adblock_parse_line (gchar* line)
adblock_frame_add_private (line, "##");
return NULL;
}
/* Got per domain CSS hider rule. Workaround */
if (strchr (line, '#'))
{
adblock_frame_add_private (line, "#");
return NULL;
}
/* Got URL blocker rule */
if (line[0] == '|' && line[1] == '|' )
{
@ -1358,16 +1405,8 @@ adblock_deactivate_cb (MidoriExtension* extension,
browser, adblock_add_tab_cb, extension);
midori_browser_foreach (browser, (GtkCallback)adblock_deactivate_tabs, browser);
if (blockcss)
g_string_free (blockcss, TRUE);
if (blockcssprivate)
g_string_free (blockcssprivate, TRUE);
adblock_destroy_db ();
midori_web_settings_remove_style (settings, "adblock-blockcss");
blockcssprivate = blockcss = NULL;
g_hash_table_destroy (pattern);
g_hash_table_destroy (optslist);
g_hash_table_destroy (urlcache);
g_object_unref (settings);
}
@ -1498,7 +1537,7 @@ extension_init (void)
MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Advertisement blocker"),
"description", _("Block advertisements according to a filter list"),
"version", "0.5" MIDORI_VERSION_SUFFIX,
"version", "0.6" MIDORI_VERSION_SUFFIX,
"authors", "Christian Dywan <christian@twotoasts.de>",
NULL);
midori_extension_install_string_list (extension, "filters", NULL, G_MAXSIZE);

View file

@ -48,11 +48,10 @@ colorful_tabs_view_notify_uri_cb (MidoriView* view,
if (!midori_uri_is_blank (midori_view_get_display_uri (view))
&& (hostname = midori_uri_parse_hostname (midori_view_get_display_uri (view), NULL))
&& katze_object_get_enum (view, "load-status") == MIDORI_LOAD_FINISHED)
&& midori_view_get_icon_uri (view) != NULL)
{
icon = midori_view_get_icon (view);
if (midori_view_get_icon_uri (view) != NULL)
if (icon != NULL)
{
GdkPixbuf* newpix;
guchar* pixels;

View file

@ -840,7 +840,8 @@ feed_panel_init (FeedPanel* panel)
webview = webkit_web_view_new ();
#if GTK_CHECK_VERSION(3,0,0)
font_desc = gtk_style_context_get_font(gtk_widget_get_style_context(treeview), GTK_STATE_FLAG_NORMAL);
font_desc = (PangoFontDescription*)gtk_style_context_get_font (
gtk_widget_get_style_context (treeview), GTK_STATE_FLAG_NORMAL);
#else
font_desc = treeview->style->font_desc;
#endif

View file

@ -1,600 +0,0 @@
/*
Copyright (C) 2009 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009 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.
*/
#define MAXCHARS 60
#define MINCHARS 2
#include <midori/midori.h>
#include <glib/gstdio.h>
#include "config.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
static GHashTable* global_keys;
static gchar* jsforms;
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser);
static gboolean
formhistory_prepare_js ()
{
gchar* autosuggest;
gchar* style;
guint i;
gchar* file;
file = sokoke_find_data_filename ("autosuggestcontrol.js", TRUE);
if (!g_file_get_contents (file, &autosuggest, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (autosuggest);
katze_assign (file, sokoke_find_data_filename ("autosuggestcontrol.css", TRUE));
if (!g_file_get_contents (file, &style, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (style);
i = 0;
while (style[i])
{
if (style[i] == '\n')
style[i] = ' ';
i++;
}
jsforms = g_strdup_printf (
"%s"
"window.addEventListener ('DOMContentLoaded',"
"function () {"
" if (document.getElementById('formhistory'))"
" return;"
" if (!initSuggestions ())"
" return;"
" var mystyle = document.createElement('style');"
" mystyle.setAttribute('type', 'text/css');"
" mystyle.setAttribute('id', 'formhistory');"
" mystyle.appendChild(document.createTextNode('%s'));"
" var head = document.getElementsByTagName('head')[0];"
" if (head) head.appendChild(mystyle);"
"}, true);",
autosuggest,
style);
g_strstrip (jsforms);
g_free (file);
g_free (style);
g_free (autosuggest);
return TRUE;
}
static gchar*
formhistory_fixup_value (char* value)
{
guint i = 0;
g_strchomp (value);
while (value[i])
{
if (value[i] == '\n')
value[i] = ' ';
else if (value[i] == '"')
value[i] = '\'';
i++;
}
return value;
}
static gchar*
formhistory_build_js ()
{
GString* suggestions;
GHashTableIter iter;
gpointer key, value;
suggestions = g_string_new (
"function FormSuggestions(eid) { "
"arr = new Array();");
g_hash_table_iter_init (&iter, global_keys);
while (g_hash_table_iter_next (&iter, &key, &value))
{
g_string_append_printf (suggestions, " arr[\"%s\"] = [%s]; ",
(gchar*)key, (gchar*)value);
}
g_string_append (suggestions, "this.suggestions = arr[eid]; }");
g_string_append (suggestions, jsforms);
return g_string_free (suggestions, FALSE);
}
static void
formhistory_update_database (gpointer db,
const gchar* key,
const gchar* value)
{
gchar* sqlcmd;
gchar* errmsg;
gint success;
sqlcmd = sqlite3_mprintf ("INSERT INTO forms VALUES"
"('%q', '%q', '%q')",
NULL, key, value);
success = sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg);
sqlite3_free (sqlcmd);
if (success != SQLITE_OK)
{
g_printerr (_("Failed to add form value: %s\n"), errmsg);
g_free (errmsg);
return;
}
}
static gboolean
formhistory_update_main_hash (gchar* key,
gchar* value)
{
guint length;
gchar* tmp;
if (!(value && *value))
return FALSE;
length = strlen (value);
if (length > MAXCHARS || length < MINCHARS)
return FALSE;
formhistory_fixup_value (key);
formhistory_fixup_value (value);
if ((tmp = g_hash_table_lookup (global_keys, (gpointer)key)))
{
gchar* rvalue = g_strdup_printf ("\"%s\"",value);
gchar* patt = g_regex_escape_string (rvalue, -1);
if (!g_regex_match_simple (patt, tmp,
G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))
{
gchar* new_value = g_strdup_printf ("%s%s,", tmp, rvalue);
g_hash_table_insert (global_keys, g_strdup (key), new_value);
g_free (rvalue);
g_free (patt);
}
else
{
g_free (rvalue);
g_free (patt);
return FALSE;
}
}
else
{
gchar* new_value = g_strdup_printf ("\"%s\",",value);
g_hash_table_replace (global_keys, g_strdup (key), new_value);
}
return TRUE;
}
static gboolean
formhistory_navigation_decision_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitNetworkRequest* request,
WebKitWebNavigationAction* action,
WebKitWebPolicyDecision* decision,
MidoriExtension* extension)
{
/* The script returns form data in the form "field_name|,|value|,|field_type".
We are handling only input fields with 'text' or 'password' type.
The field separator is "|||" */
const gchar* script = "function dumpForm (inputs) {"
" var out = '';"
" for (i=0;i<inputs.length;i++) {"
" if (inputs[i].getAttribute('autocomplete') == 'off')"
" continue;"
" if (inputs[i].value && (inputs[i].type == 'text' || inputs[i].type == 'password')) {"
" var ename = inputs[i].getAttribute('name');"
" var eid = inputs[i].getAttribute('id');"
" if (!ename && eid)"
" ename=eid;"
" if (inputs[i].getAttribute('autocomplete') != 'off')"
" out += ename+'|,|'+inputs[i].value +'|,|'+inputs[i].type +'|||';"
" }"
" }"
" return out;"
"}"
"dumpForm (document.getElementsByTagName('input'))";
if (webkit_web_navigation_action_get_reason (action) == WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED)
{
JSContextRef js_context = webkit_web_frame_get_global_context (web_frame);
gchar* value = sokoke_js_script_eval (js_context, script, NULL);
if (value && *value)
{
gpointer db = g_object_get_data (G_OBJECT (extension), "formhistory-db");
gchar** inputs = g_strsplit (value, "|||", 0);
guint i = 0;
while (inputs[i] != NULL)
{
gchar** parts = g_strsplit (inputs[i], "|,|", 3);
if (parts && parts[0] && parts[1] && parts[2])
{
/* FIXME: We need to handle passwords */
if (strcmp (parts[2], "password"))
{
if (formhistory_update_main_hash (parts[0], parts[1]))
formhistory_update_database (db, parts[0], parts[1]);
}
}
g_strfreev (parts);
i++;
}
g_strfreev (inputs);
g_free (value);
}
}
return FALSE;
}
static void
formhistory_window_object_cleared_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
JSContextRef js_context,
JSObjectRef js_window)
{
gchar* script;
const gchar* page_uri;
page_uri = webkit_web_frame_get_uri (web_frame);
if (!midori_uri_is_http (page_uri))
return;
script = formhistory_build_js ();
sokoke_js_script_eval (js_context, script, NULL);
g_free (script);
}
static void
formhistory_add_tab_cb (MidoriBrowser* browser,
MidoriView* view,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_connect (web_view, "window-object-cleared",
G_CALLBACK (formhistory_window_object_cleared_cb), NULL);
g_signal_connect (web_view, "navigation-policy-decision-requested",
G_CALLBACK (formhistory_navigation_decision_cb), extension);
}
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser);
static void
formhistory_add_tab_foreach_cb (MidoriView* view,
MidoriBrowser* browser,
MidoriExtension* extension)
{
formhistory_add_tab_cb (browser, view, extension);
}
static void
formhistory_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkAccelGroup* acg = gtk_accel_group_new ();
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action = gtk_action_new ("FormHistoryToggleState",
_("Toggle form history state"),
_("Activate or deactivate form history for the current tab."), NULL);
gtk_window_add_accel_group (GTK_WINDOW (browser), acg);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", extension);
g_signal_connect (action, "activate",
G_CALLBACK (formhistory_toggle_state_cb), browser);
gtk_action_group_add_action_with_accel (action_group, action, "<Ctrl><Shift>F");
gtk_action_set_accel_group (action, acg);
gtk_action_connect_accelerator (action);
if (midori_extension_get_boolean (extension, "always-load"))
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_add_tab_foreach_cb, extension);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
g_signal_connect (extension, "deactivate",
G_CALLBACK (formhistory_deactivate_cb), browser);
}
static void
formhistory_deactivate_tabs (MidoriView* view,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_window_object_cleared_cb, NULL);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_navigation_decision_cb, extension);
}
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser)
{
MidoriApp* app = midori_extension_get_app (extension);
sqlite3* db;
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action;
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
g_signal_handlers_disconnect_by_func (
extension, formhistory_deactivate_cb, browser);
g_signal_handlers_disconnect_by_func (
app, formhistory_app_add_browser_cb, extension);
midori_browser_foreach (browser,
(GtkCallback)formhistory_deactivate_tabs, extension);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", NULL);
action = gtk_action_group_get_action ( action_group, "FormHistoryToggleState");
if (action != NULL)
{
gtk_action_group_remove_action (action_group, action);
g_object_unref (action);
}
katze_assign (jsforms, NULL);
if (global_keys)
g_hash_table_destroy (global_keys);
if ((db = g_object_get_data (G_OBJECT (extension), "formhistory-db")))
sqlite3_close (db);
}
static int
formhistory_add_field (gpointer data,
int argc,
char** argv,
char** colname)
{
gint i;
gint ncols = 3;
/* Test whether have the right number of columns */
g_return_val_if_fail (argc % ncols == 0, 1);
for (i = 0; i < (argc - ncols) + 1; i++)
{
if (argv[i])
{
if (colname[i] && !g_ascii_strcasecmp (colname[i], "domain")
&& colname[i + 1] && !g_ascii_strcasecmp (colname[i + 1], "field")
&& colname[i + 2] && !g_ascii_strcasecmp (colname[i + 2], "value"))
{
gchar* key = argv[i + 1];
formhistory_update_main_hash (g_strdup (key), g_strdup (argv[i + 2]));
}
}
}
return 0;
}
static void
formhistory_activate_cb (MidoriExtension* extension,
MidoriApp* app)
{
const gchar* config_dir;
gchar* filename;
sqlite3* db;
char* errmsg = NULL, *errmsg2 = NULL;
KatzeArray* browsers;
MidoriBrowser* browser;
global_keys = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
(GDestroyNotify)g_free);
if(!jsforms)
formhistory_prepare_js ();
config_dir = midori_extension_get_config_dir (extension);
katze_mkdir_with_parents (config_dir, 0700);
filename = g_build_filename (config_dir, "forms.db", NULL);
if (sqlite3_open (filename, &db) != SQLITE_OK)
{
/* If the folder is /, this is a test run, thus no error */
if (!g_str_equal (midori_extension_get_config_dir (extension), "/"))
g_warning (_("Failed to open database: %s\n"), sqlite3_errmsg (db));
sqlite3_close (db);
}
g_free (filename);
if ((sqlite3_exec (db, "CREATE TABLE IF NOT EXISTS "
"forms (domain text, field text, value text)",
NULL, NULL, &errmsg) == SQLITE_OK)
&& (sqlite3_exec (db, "SELECT domain, field, value FROM forms ",
formhistory_add_field,
NULL, &errmsg2) == SQLITE_OK))
g_object_set_data (G_OBJECT (extension), "formhistory-db", db);
else
{
if (errmsg)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg);
sqlite3_free (errmsg);
if (errmsg2)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg2);
sqlite3_free (errmsg2);
}
}
sqlite3_close (db);
}
browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
formhistory_app_add_browser_cb (app, browser, extension);
g_signal_connect (app, "add-browser",
G_CALLBACK (formhistory_app_add_browser_cb), extension);
g_object_unref (browsers);
}
static void
formhistory_preferences_response_cb (GtkWidget* dialog,
gint response_id,
MidoriExtension* extension)
{
GtkWidget* checkbox;
gboolean old_state;
gboolean new_state;
MidoriApp* app;
KatzeArray* browsers;
MidoriBrowser* browser;
if (response_id == GTK_RESPONSE_APPLY)
{
checkbox = g_object_get_data (G_OBJECT (dialog), "always-load-checkbox");
new_state = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
old_state = midori_extension_get_boolean (extension, "always-load");
if (old_state != new_state)
{
midori_extension_set_boolean (extension, "always-load", new_state);
app = midori_extension_get_app (extension);
browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_deactivate_tabs, extension);
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
if (new_state)
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_add_tab_foreach_cb, extension);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
}
}
}
gtk_widget_destroy (dialog);
}
static void
formhistory_preferences_cb (MidoriExtension* extension)
{
GtkWidget* dialog;
GtkWidget* content_area;
GtkWidget* checkbox;
dialog = gtk_dialog_new ();
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
checkbox = gtk_check_button_new_with_label (_("only activate form history via hotkey (Ctrl+Shift+F) per tab"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
!midori_extension_get_boolean (extension, "always-load"));
g_object_set_data (G_OBJECT (dialog), "always-load-checkbox", checkbox);
gtk_container_add (GTK_CONTAINER (content_area), checkbox);
g_signal_connect (dialog,
"response",
G_CALLBACK (formhistory_preferences_response_cb),
extension);
gtk_widget_show_all (dialog);
}
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser)
{
MidoriView* view = MIDORI_VIEW (midori_browser_get_current_tab (browser));
MidoriExtension* extension = g_object_get_data (G_OBJECT (browser), "FormHistoryExtension");
GtkWidget* web_view = midori_view_get_web_view (view);
if (g_signal_handler_find (web_view, G_SIGNAL_MATCH_FUNC,
g_signal_lookup ("window-object-cleared", MIDORI_TYPE_VIEW), 0, NULL,
formhistory_window_object_cleared_cb, extension))
{
formhistory_deactivate_tabs (view, browser, extension);
} else {
formhistory_add_tab_cb (browser, view, extension);
}
}
#if G_ENABLE_DEBUG
/*
<html>
<head>
<title>autosuggest testcase</title>
</head>
<body>
<form method=post>
<p><input type="text" id="txt1" /></p>
<p><input type="text" name="txt2" /></p>
<input type=submit>
</form>
</body>
</html> */
#endif
MidoriExtension*
extension_init (void)
{
gboolean should_init = TRUE;
const gchar* ver;
gchar* desc;
MidoriExtension* extension;
if (formhistory_prepare_js ())
{
ver = "1.0" MIDORI_VERSION_SUFFIX;
desc = g_strdup (_("Stores history of entered form data"));
}
else
{
desc = g_strdup_printf (_("Not available: %s"),
_("Resource files not installed"));
ver = NULL;
should_init = FALSE;
}
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Form history filler"),
"description", desc,
"version", ver,
"authors", "Alexander V. Butenko <a.butenka@gmail.com>",
NULL);
g_free (desc);
if (should_init)
{
midori_extension_install_boolean (extension, "always-load", TRUE);
g_signal_connect (extension, "activate",
G_CALLBACK (formhistory_activate_cb), NULL);
g_signal_connect (extension, "open-preferences",
G_CALLBACK (formhistory_preferences_cb), NULL);
}
return extension;
}

View file

@ -0,0 +1,74 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-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.
*/
#ifndef __FORMHISTORY_FRONTEND_H__
#define __FORMHISTORY_FRONTEND_H__
#include <midori/midori.h>
#include <glib/gstdio.h>
#include "config.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if WEBKIT_CHECK_VERSION (1, 3, 1)
#define FORMHISTORY_USE_GDOM 1
#else
#define FORMHISTORY_USE_JS 1
#endif
#define MAXPASSSIZE 64
typedef struct
{
sqlite3* db;
#ifdef FORMHISTORY_USE_GDOM
WebKitDOMElement* element;
int completion_timeout;
GtkTreeModel* completion_model;
GtkWidget* treeview;
GtkWidget* popup;
gchar* oldkeyword;
glong selection_index;
#else
gchar* jsforms;
#endif
gchar* master_password;
int master_password_canceled;
} FormHistoryPriv;
typedef struct
{
gchar* domain;
gchar* form_data;
FormHistoryPriv* priv;
} FormhistoryPasswordEntry;
FormHistoryPriv*
formhistory_private_new ();
void
formhistory_private_destroy (FormHistoryPriv *priv);
gboolean
formhistory_construct_popup_gui (FormHistoryPriv* priv);
void
formhistory_setup_suggestions (WebKitWebView* web_view,
JSContextRef js_context,
MidoriExtension* extension);
#ifdef FORMHISTORY_USE_GDOM
void
formhistory_suggestions_hide_cb (WebKitDOMElement* element,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv);
#endif
#endif

View file

@ -0,0 +1,516 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-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.
*/
#include "formhistory-frontend.h"
#ifdef FORMHISTORY_USE_GDOM
#define COMPLETION_DELAY 200
FormHistoryPriv*
formhistory_private_new ()
{
FormHistoryPriv* priv;
priv = g_slice_new (FormHistoryPriv);
priv->oldkeyword = g_strdup ("");
priv->selection_index = -1;
return priv;
}
void
formhistory_suggestions_hide_cb (WebKitDOMElement* element,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv)
{
if (gtk_widget_get_visible (priv->popup))
gtk_widget_hide (priv->popup);
priv->selection_index = -1;
}
static void
formhistory_suggestion_set (GtkTreePath* path,
FormHistoryPriv* priv)
{
GtkTreeIter iter;
gchar* value;
if (!gtk_tree_model_get_iter (priv->completion_model, &iter, path))
return;
gtk_tree_model_get (priv->completion_model, &iter, 0, &value, -1);
g_object_set (priv->element, "value", value, NULL);
g_free (value);
}
static gboolean
formhistory_suggestion_selected_cb (GtkWidget* treeview,
GdkEventButton* event,
FormHistoryPriv* priv)
{
GtkTreePath* path;
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview),
event->x, event->y, &path, NULL, NULL, NULL))
{
formhistory_suggestion_set (path, priv);
formhistory_suggestions_hide_cb (NULL, NULL, priv);
gtk_tree_path_free (path);
return TRUE;
}
return FALSE;
}
static void
formhistory_suggestion_remove (GtkTreePath* path,
FormHistoryPriv* priv)
{
GtkTreeIter iter;
gchar* sqlcmd;
char* errmsg = NULL;
gchar* name;
gchar* value;
if (!gtk_tree_model_get_iter (priv->completion_model, &iter, path))
return;
if (!priv->db)
return;
gtk_tree_model_get (priv->completion_model, &iter, 0, &value, -1);
g_object_get (priv->element, "name", &name, NULL);
gtk_list_store_remove (GTK_LIST_STORE (priv->completion_model), &iter);
sqlcmd = sqlite3_mprintf ("DELETE FROM forms WHERE field = '%q' AND value = '%q'",
name, value);
g_free (name);
g_free (value);
sqlite3_exec (priv->db, sqlcmd, NULL, NULL, &errmsg);
sqlite3_free (sqlcmd);
}
static void
get_absolute_offset_for_element (WebKitDOMElement* element,
WebKitDOMDocument* element_document,
WebKitDOMNodeList* frames,
glong* x,
glong* y,
gboolean ismainframe)
{
WebKitDOMElement* offset_parent;
gint offset_top = 0, offset_left = 0;
gulong i;
g_object_get (element, "offset-left", &offset_left,
"offset-top", &offset_top,
"offset-parent", &offset_parent,
NULL);
*x += offset_left;
*y += offset_top;
/* To avoid deadlock check only first element of the mainframe parent */
if (ismainframe == TRUE)
return;
if (offset_parent)
goto finish;
/* Element havent returned any parents. Thats mean or there is no parents or we are inside the frame
Loop over all frames we have to find frame == element_document which is a root for our element
and get its offsets */
for (i = 0; i < webkit_dom_node_list_get_length (frames); i++)
{
WebKitDOMDocument *fdoc;
WebKitDOMNode *frame = webkit_dom_node_list_item (frames, i);
if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (frame))
fdoc = webkit_dom_html_iframe_element_get_content_document (WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame));
else
fdoc = webkit_dom_html_frame_element_get_content_document (WEBKIT_DOM_HTML_FRAME_ELEMENT (frame));
if (fdoc == element_document)
{
offset_parent = WEBKIT_DOM_ELEMENT (frame);
ismainframe = TRUE;
/* Add extra 4px to ~cover size of borders */
*y += 4;
break;
}
}
finish:
if (offset_parent)
get_absolute_offset_for_element (offset_parent, element_document, frames, x, y, ismainframe);
}
static void
formhistory_reposition_popup (FormHistoryPriv* priv)
{
WebKitDOMDocument* element_document;
WebKitDOMNodeList* frames;
GtkWidget* view;
GdkWindow* window;
GtkWidget* toplevel;
gint rx, ry;
gint wx, wy;
glong x = 0, y = 0;
glong height;
view = g_object_get_data (G_OBJECT (priv->element), "webview");
toplevel = gtk_widget_get_toplevel (view);
/* Position of a root window */
window = gtk_widget_get_window (toplevel);
gdk_window_get_position (window, &rx, &ry);
/* Postion of webview in root window */
window = gtk_widget_get_window (view);
gdk_window_get_position (window, &wx, &wy);
/* Position of editbox on the webview */
frames = g_object_get_data (G_OBJECT (priv->element), "framelist");
element_document = g_object_get_data (G_OBJECT (priv->element), "doc");
get_absolute_offset_for_element (priv->element, element_document, frames, &x, &y, FALSE);
/* Add height as menu should start under editbox, now on top of it */
g_object_get (priv->element, "client-height", &height, NULL);
y += height + 1;
gtk_window_move (GTK_WINDOW (priv->popup), rx + wx + x, ry +wy + y);
/* Window configuration */
gtk_window_set_screen (GTK_WINDOW (priv->popup), gtk_widget_get_screen (view));
gtk_window_set_transient_for (GTK_WINDOW (priv->popup), GTK_WINDOW (toplevel));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (priv->treeview));
/* FIXME: Adjust size according to treeview width and some reasonable height */
gtk_window_resize (GTK_WINDOW (priv->popup), 50, 80);
}
static gboolean
formhistory_suggestions_show (FormHistoryPriv* priv)
{
GtkListStore* store;
static sqlite3_stmt* stmt;
gchar* value, * name;
const char* sqlcmd;
gint result;
gchar* likedvalue;
int pos = 0;
g_return_val_if_fail (priv->element, FALSE);
g_object_get (priv->element,
"name", &name,
"value", &value,
NULL);
katze_assign (priv->oldkeyword, g_strdup (value));
if (!priv->popup)
formhistory_construct_popup_gui (priv);
if (!stmt)
{
if (!priv->db)
goto free_data;
sqlcmd = "SELECT DISTINCT value FROM forms WHERE field = ?1 and value like ?2";
sqlite3_prepare_v2 (priv->db, sqlcmd, strlen (sqlcmd) + 1, &stmt, NULL);
}
likedvalue = g_strdup_printf ("%s%%", value);
sqlite3_bind_text (stmt, 1, name, -1, NULL);
sqlite3_bind_text (stmt, 2, likedvalue, -1, g_free);
result = sqlite3_step (stmt);
if (result != SQLITE_ROW)
{
if (result == SQLITE_ERROR)
g_print (_("Failed to select suggestions\n"));
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
formhistory_suggestions_hide_cb (NULL, NULL, priv);
goto free_data;
}
store = GTK_LIST_STORE (priv->completion_model);
gtk_list_store_clear (store);
while (result == SQLITE_ROW)
{
const unsigned char* text = sqlite3_column_text (stmt, 0);
pos++;
gtk_list_store_insert_with_values (store, NULL, pos, 0, text, -1);
result = sqlite3_step (stmt);
}
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
if (!gtk_widget_get_visible (priv->popup))
{
formhistory_reposition_popup (priv);
gtk_widget_show_all (priv->popup);
}
free_data:
g_free (name);
g_free (value);
return FALSE;
}
static void
formhistory_editbox_key_pressed_cb (WebKitDOMElement* element,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv)
{
glong key;
GtkTreePath* path;
gchar* keyword;
gint matches;
/* FIXME: Priv is still set after module is disabled */
g_return_if_fail (priv);
g_return_if_fail (element);
if (priv->completion_timeout > 0)
g_source_remove (priv->completion_timeout);
katze_object_assign (priv->element, g_object_ref (element));
key = webkit_dom_ui_event_get_key_code (WEBKIT_DOM_UI_EVENT (dom_event));
switch (key)
{
/* ESC key*/
case 27:
case 35:
case 36:
/* Left key*/
case 37:
/* Right key*/
case 39:
/* Enter key*/
case 13:
if (key == 27)
g_object_set (element, "value", priv->oldkeyword, NULL);
formhistory_suggestions_hide_cb (element, dom_event, priv);
return;
break;
/* Del key */
case 46:
/* Up key */
case 38:
/* Down key */
case 40:
if (!gtk_widget_get_visible (priv->popup))
{
formhistory_suggestions_show (priv);
return;
}
matches = gtk_tree_model_iter_n_children (priv->completion_model, NULL);
if (key == 38)
{
if (priv->selection_index <= 0)
priv->selection_index = matches - 1;
else
priv->selection_index = MAX (priv->selection_index - 1, 0);
}
else if (key == 40)
{
if (priv->selection_index == matches - 1)
priv->selection_index = 0;
else
priv->selection_index = MIN (priv->selection_index + 1, matches -1);
}
if (priv->selection_index == -1)
{
/* No element is selected */
return;
}
path = gtk_tree_path_new_from_indices (priv->selection_index, -1);
if (key == 46)
{
g_object_set (element, "value", priv->oldkeyword, NULL);
formhistory_suggestion_remove (path, priv);
matches--;
}
if (matches == 0)
formhistory_suggestions_hide_cb (element, dom_event, priv);
else
{
gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->treeview), path, NULL, FALSE);
formhistory_suggestion_set (path, priv);
}
gtk_tree_path_free (path);
return;
break;
/* PgUp, PgDn, Ins */
case 33:
case 34:
case 45:
/* Shift, Ctrl, Alt, Tab, Caps Lock*/
case 16:
case 17:
case 18:
case 20:
case 9:
return;
break;
}
g_object_get (element, "value", &keyword, NULL);
if (!(keyword && *keyword && *keyword != ' '))
{
formhistory_suggestions_hide_cb (element, dom_event, priv);
goto free_data;
}
/* If the same keyword is submitted there's no need to regenerate suggestions */
if (gtk_widget_get_visible (priv->popup) &&
!g_strcmp0 (keyword, priv->oldkeyword))
goto free_data;
priv->completion_timeout = g_timeout_add (COMPLETION_DELAY,
(GSourceFunc)formhistory_suggestions_show, priv);
free_data:
g_free (keyword);
}
static void
formhistory_DOMContentLoaded_cb (WebKitDOMElement* window,
WebKitDOMEvent* dom_event,
FormHistoryPriv* priv)
{
gulong i;
WebKitDOMDocument* doc;
WebKitDOMNodeList* inputs;
WebKitDOMNodeList* frames;
GtkWidget* web_view;
if (WEBKIT_DOM_IS_DOCUMENT (window))
doc = WEBKIT_DOM_DOCUMENT (window);
else
doc = webkit_dom_dom_window_get_document (WEBKIT_DOM_DOM_WINDOW (window));
inputs = webkit_dom_document_query_selector_all (doc, "input[type='text']", NULL);
frames = g_object_get_data (G_OBJECT (window), "framelist");
web_view = g_object_get_data (G_OBJECT (window), "webview");
for (i = 0; i < webkit_dom_node_list_get_length (inputs); i++)
{
WebKitDOMNode* element = webkit_dom_node_list_item (inputs, i);
#if WEBKIT_CHECK_VERSION (1, 6, 1)
gchar* autocomplete = webkit_dom_html_input_element_get_autocomplete (
WEBKIT_DOM_HTML_INPUT_ELEMENT (element));
gboolean off = !g_strcmp0 (autocomplete, "off");
g_free (autocomplete);
if (off)
continue;
#endif
g_object_set_data (G_OBJECT (element), "doc", doc);
g_object_set_data (G_OBJECT (element), "webview", web_view);
g_object_set_data (G_OBJECT (element), "framelist", frames);
/* Add dblclick? */
webkit_dom_event_target_add_event_listener (
WEBKIT_DOM_EVENT_TARGET (element), "keyup",
G_CALLBACK (formhistory_editbox_key_pressed_cb), false,
priv);
webkit_dom_event_target_add_event_listener (
WEBKIT_DOM_EVENT_TARGET (element), "blur",
G_CALLBACK (formhistory_suggestions_hide_cb), false,
priv);
}
}
void
formhistory_setup_suggestions (WebKitWebView* web_view,
JSContextRef js_context,
MidoriExtension* extension)
{
WebKitDOMDocument* doc;
WebKitDOMNodeList* frames;
gulong i;
FormHistoryPriv* priv = g_object_get_data (G_OBJECT (extension), "priv");
doc = webkit_web_view_get_dom_document (web_view);
frames = webkit_dom_document_query_selector_all (doc, "iframe, frame", NULL);
g_object_set_data (G_OBJECT (doc), "framelist", frames);
g_object_set_data (G_OBJECT (doc), "webview", web_view);
/* Connect to DOMContentLoaded of the main frame */
webkit_dom_event_target_add_event_listener(
WEBKIT_DOM_EVENT_TARGET (doc), "DOMContentLoaded",
G_CALLBACK (formhistory_DOMContentLoaded_cb), false,
priv);
/* Connect to DOMContentLoaded of frames */
for (i = 0; i < webkit_dom_node_list_get_length (frames); i++)
{
WebKitDOMDOMWindow* framewin;
WebKitDOMNode* frame = webkit_dom_node_list_item (frames, i);
if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (frame))
framewin = webkit_dom_html_iframe_element_get_content_window (WEBKIT_DOM_HTML_IFRAME_ELEMENT (frame));
else
framewin = webkit_dom_html_frame_element_get_content_window (WEBKIT_DOM_HTML_FRAME_ELEMENT (frame));
g_object_set_data (G_OBJECT (framewin), "framelist", frames);
g_object_set_data (G_OBJECT (framewin), "webview", (GtkWidget*)web_view);
webkit_dom_event_target_add_event_listener (
WEBKIT_DOM_EVENT_TARGET (framewin), "DOMContentLoaded",
G_CALLBACK (formhistory_DOMContentLoaded_cb), false,
priv);
}
formhistory_suggestions_hide_cb (NULL, NULL, priv);
}
void
formhistory_private_destroy (FormHistoryPriv *priv)
{
if (priv->db)
{
sqlite3_close (priv->db);
priv->db = NULL;
}
katze_assign (priv->oldkeyword, NULL);
gtk_widget_destroy (priv->popup);
priv->popup = NULL;
katze_object_assign (priv->element, NULL);
g_slice_free (FormHistoryPriv, priv);
}
gboolean
formhistory_construct_popup_gui (FormHistoryPriv* priv)
{
GtkTreeModel* model = NULL;
GtkWidget* popup;
GtkWidget* popup_frame;
GtkWidget* scrolled;
GtkWidget* treeview;
GtkCellRenderer* renderer;
GtkTreeViewColumn* column;
model = (GtkTreeModel*) gtk_list_store_new (1, G_TYPE_STRING);
priv->completion_model = model;
popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_type_hint (GTK_WINDOW (popup), GDK_WINDOW_TYPE_HINT_COMBO);
popup_frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (popup_frame), GTK_SHADOW_ETCHED_IN);
gtk_container_add (GTK_CONTAINER (popup), popup_frame);
scrolled = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
"hscrollbar-policy", GTK_POLICY_NEVER,
"vscrollbar-policy", GTK_POLICY_AUTOMATIC, NULL);
gtk_container_add (GTK_CONTAINER (popup_frame), scrolled);
treeview = gtk_tree_view_new_with_model (model);
priv->treeview = treeview;
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (treeview), TRUE);
gtk_container_add (GTK_CONTAINER (scrolled), treeview);
gtk_widget_set_size_request (gtk_scrolled_window_get_vscrollbar (
GTK_SCROLLED_WINDOW (scrolled)), -1, 0);
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("suggestions", renderer, "text", 0, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
priv->popup = popup;
g_signal_connect (treeview, "button-press-event",
G_CALLBACK (formhistory_suggestion_selected_cb), priv);
return TRUE;
}
#endif

View file

@ -0,0 +1,143 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-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.
*/
#include "formhistory-frontend.h"
#ifdef FORMHISTORY_USE_JS
FormHistoryPriv*
formhistory_private_new ()
{
FormHistoryPriv* priv;
priv = g_slice_new (FormHistoryPriv);
return priv;
}
gboolean
formhistory_construct_popup_gui (FormHistoryPriv* priv)
{
gchar* autosuggest;
gchar* style;
guint i;
gchar* file;
file = sokoke_find_data_filename ("autosuggestcontrol.js", TRUE);
if (!g_file_get_contents (file, &autosuggest, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (autosuggest);
katze_assign (file, sokoke_find_data_filename ("autosuggestcontrol.css", TRUE));
if (!g_file_get_contents (file, &style, NULL, NULL))
{
g_free (file);
return FALSE;
}
g_strchomp (style);
g_free (file);
i = 0;
while (style[i])
{
if (style[i] == '\n')
style[i] = ' ';
i++;
}
priv->jsforms = g_strdup_printf (
"%s"
"window.addEventListener ('DOMContentLoaded',"
"function () {"
" if (document.getElementById('formhistory'))"
" return;"
" if (!initSuggestions ())"
" return;"
" var mystyle = document.createElement('style');"
" mystyle.setAttribute('type', 'text/css');"
" mystyle.setAttribute('id', 'formhistory');"
" mystyle.appendChild(document.createTextNode('%s'));"
" var head = document.getElementsByTagName('head')[0];"
" if (head) head.appendChild(mystyle);"
"}, true);",
autosuggest,
style);
g_strstrip (priv->jsforms);
g_free (style);
g_free (autosuggest);
return TRUE;
}
void
formhistory_setup_suggestions (WebKitWebView* web_view,
JSContextRef js_context,
MidoriExtension* extension)
{
GString* suggestions;
FormHistoryPriv* priv;
static sqlite3_stmt* stmt;
const char* sqlcmd;
const unsigned char* key;
const unsigned char* value;
gint result, pos;
priv = g_object_get_data (G_OBJECT (extension), "priv");
if (!priv->db)
return;
if (!stmt)
{
sqlcmd = "SELECT DISTINCT group_concat(value,'\",\"'), field FROM forms \
GROUP BY field ORDER BY field";
sqlite3_prepare_v2 (priv->db, sqlcmd, strlen (sqlcmd) + 1, &stmt, NULL);
}
result = sqlite3_step (stmt);
if (result != SQLITE_ROW)
{
if (result == SQLITE_ERROR)
g_print (_("Failed to select suggestions\n"));
sqlite3_reset (stmt);
return;
}
suggestions = g_string_new (
"function FormSuggestions(eid) { "
"arr = new Array();");
while (result == SQLITE_ROW)
{
pos++;
value = sqlite3_column_text (stmt, 0);
key = sqlite3_column_text (stmt, 1);
if (value)
{
g_string_append_printf (suggestions, " arr[\"%s\"] = [\"%s\"]; ",
(gchar*)key, (gchar*)value);
}
result = sqlite3_step (stmt);
}
g_string_append (suggestions, "this.suggestions = arr[eid]; }");
g_string_append (suggestions, priv->jsforms);
sokoke_js_script_eval (js_context, suggestions->str, NULL);
g_string_free (suggestions, TRUE);
}
void
formhistory_private_destroy (FormHistoryPriv *priv)
{
if (priv->db)
{
sqlite3_close (priv->db);
priv->db = NULL;
}
katze_assign (priv->jsforms, NULL);
g_slice_free (FormHistoryPriv, priv);
}
#endif

View file

@ -0,0 +1,679 @@
/*
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
Copyright (C) 2009-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.
*/
#define MAXCHARS 60
#define MINCHARS 2
#define GTK_RESPONSE_IGNORE 99
#include "formhistory-frontend.h"
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser);
static void
formhistory_update_database (gpointer db,
const gchar* host,
const gchar* key,
const gchar* value)
{
gchar* sqlcmd;
gchar* errmsg;
gint success;
if (!(value && *value))
return;
sqlcmd = sqlite3_mprintf ("INSERT INTO forms VALUES"
"('%q', '%q', '%q')",
host, key, value);
success = sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg);
sqlite3_free (sqlcmd);
if (success != SQLITE_OK)
{
g_printerr (_("Failed to add form value: %s\n"), errmsg);
g_free (errmsg);
return;
}
}
static gchar*
formhistory_get_login_data (gpointer db,
const gchar* domain)
{
static sqlite3_stmt* stmt;
const char* sqlcmd;
gint result;
gchar* value = NULL;
if (!stmt)
{
sqlcmd = "SELECT value FROM forms WHERE domain = ?1 and field = 'MidoriPasswordManager' limit 1";
sqlite3_prepare_v2 (db, sqlcmd, strlen (sqlcmd) + 1, &stmt, NULL);
}
sqlite3_bind_text (stmt, 1, domain, -1, NULL);
result = sqlite3_step (stmt);
if (result == SQLITE_ROW)
value = g_strdup ((gchar*)sqlite3_column_text (stmt, 0));
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
return value;
}
static gboolean
formhistory_check_master_password (GtkWidget* parent,
FormHistoryPriv* priv)
{
GtkWidget* dialog;
GtkWidget* content_area;
GtkWidget* hbox;
GtkWidget* image;
GtkWidget* label;
GtkWidget* entry;
const gchar* title;
static int alive;
gboolean ret = FALSE;
/* Password is set */
if (priv->master_password && *priv->master_password)
return TRUE;
/* Other prompt is active */
if (alive == 1)
return FALSE;
/* Prompt was cancelled */
if (priv->master_password_canceled == 1)
return FALSE;
alive = 1;
title = _("Form history");
dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parent),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_DIALOG_AUTHENTICATION);
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
gtk_container_set_border_width (GTK_CONTAINER (content_area), 5);
hbox = gtk_hbox_new (FALSE, 8);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
label = gtk_label_new (_("Master password required\n"
"to open password database"));
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (content_area), hbox);
entry = gtk_entry_new ();
g_object_set (entry, "truncate-multiline", TRUE, NULL);
gtk_entry_set_visibility(GTK_ENTRY (entry),FALSE);
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
gtk_container_add (GTK_CONTAINER (content_area), entry);
gtk_widget_show_all (entry);
gtk_widget_show_all (hbox);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
{
/* FIXME: add password verification */
katze_assign (priv->master_password,
g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))));
ret = TRUE;
}
else
priv->master_password_canceled = 1;
gtk_widget_destroy (dialog);
alive = 0;
return ret;
}
static gchar*
formhistory_encrypt (const gchar* data,
const gchar* password)
{
/* TODO: Implement persistent storage/ keyring support */
return NULL;
}
static void
formhistory_remember_password_response (GtkWidget* infobar,
gint response_id,
FormhistoryPasswordEntry* entry)
{
gchar* encrypted_form;
if (response_id == GTK_RESPONSE_IGNORE)
goto cleanup;
if (formhistory_check_master_password (NULL, entry->priv))
{
if (response_id != GTK_RESPONSE_ACCEPT)
katze_assign (entry->form_data, g_strdup ("never"));
if ((encrypted_form = formhistory_encrypt (entry->form_data,
entry->priv->master_password)))
formhistory_update_database (entry->priv->db, entry->domain, "MidoriPasswordManager", encrypted_form);
g_free (encrypted_form);
}
cleanup:
g_free (entry->form_data);
g_free (entry->domain);
g_slice_free (FormhistoryPasswordEntry, entry);
gtk_widget_destroy (infobar);
}
static gboolean
formhistory_navigation_decision_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
WebKitNetworkRequest* request,
WebKitWebNavigationAction* action,
WebKitWebPolicyDecision* decision,
MidoriExtension* extension)
{
FormHistoryPriv* priv;
JSContextRef js_context;
gchar* value;
/* The script returns form data in the form "field_name|,|value|,|field_type".
We are handling only input fields with 'text' or 'password' type.
The field separator is "|||" */
const gchar* script = "function dumpForm (inputs) {"
" var out = '';"
" for (i=0;i<inputs.length;i++) {"
" if (inputs[i].getAttribute('autocomplete') == 'off' && "
" inputs[i].type == 'text')"
" continue;"
" if (inputs[i].value && (inputs[i].type == 'text' || inputs[i].type == 'password')) {"
" var ename = inputs[i].getAttribute('name');"
" var eid = inputs[i].getAttribute('id');"
" if (!eid && ename)"
" eid=ename;"
" out += eid+'|,|'+inputs[i].value +'|,|'+inputs[i].type +'|||';"
" }"
" }"
" return out;"
"}"
"dumpForm (document.getElementsByTagName('input'))";
if (webkit_web_navigation_action_get_reason (action) != WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED)
return FALSE;
priv = g_object_get_data (G_OBJECT (extension), "priv");
js_context = webkit_web_frame_get_global_context (web_frame);
value = sokoke_js_script_eval (js_context, script, NULL);
formhistory_suggestions_hide_cb (NULL, NULL, priv);
if (value && *value)
{
gchar** inputs = g_strsplit (value, "|||", 0);
guint i = 0;
while (inputs[i] != NULL)
{
gchar** parts = g_strsplit (inputs[i], "|,|", 3);
if (parts && parts[0] && parts[1] && parts[2])
{
if (strcmp (parts[2], "password"))
formhistory_update_database (priv->db, NULL, parts[0], parts[1]);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
else
{
gchar* data;
gchar* domain;
#if 0
FormhistoryPasswordEntry* entry;
#endif
domain = midori_uri_parse_hostname (webkit_web_frame_get_uri (web_frame), NULL);
data = formhistory_get_login_data (priv->db, domain);
if (data)
{
g_free (data);
g_free (domain);
break;
}
#if 0
entry = g_slice_new (FormhistoryPasswordEntry);
/* Domain and form data are freed from infopanel callback*/
entry->form_data = g_strdup (value);
entry->domain = domain;
entry->priv = priv;
g_object_set_data (G_OBJECT (web_view), "FormHistoryPasswordEntry", entry);
#endif
}
#endif
}
g_strfreev (parts);
i++;
}
g_strfreev (inputs);
g_free (value);
}
return FALSE;
}
static void
formhistory_window_object_cleared_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
JSContextRef js_context,
JSObjectRef js_window,
MidoriExtension* extension)
{
const gchar* page_uri;
FormhistoryPasswordEntry* entry;
GtkWidget* view;
page_uri = webkit_web_frame_get_uri (web_frame);
if (!page_uri)
return;
if (!midori_uri_is_http (page_uri) && !g_str_has_prefix (page_uri, "file"))
return;
formhistory_setup_suggestions (web_view, js_context, extension);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
entry = g_object_get_data (G_OBJECT (web_view), "FormHistoryPasswordEntry");
if (entry)
{
const gchar* message = _("Remember password on this page?");
view = midori_browser_get_current_tab (midori_app_get_browser (
midori_extension_get_app (extension)));
midori_view_add_info_bar (MIDORI_VIEW (view), GTK_MESSAGE_QUESTION, message,
G_CALLBACK (formhistory_remember_password_response), entry,
_("Remember"), GTK_RESPONSE_ACCEPT,
_("Not now"), GTK_RESPONSE_IGNORE,
_("Never for this page"), GTK_RESPONSE_CANCEL, NULL);
g_object_set_data (G_OBJECT (web_view), "FormHistoryPasswordEntry", NULL);
}
#endif
}
#if WEBKIT_CHECK_VERSION (1, 3, 8)
static gchar*
formhistory_decrypt (const gchar* data,
const gchar* password)
{
/* TODO: Implement persistent storage/ keyring support */
return NULL;
}
static void
formhistory_fill_login_data (JSContextRef js_context,
FormHistoryPriv* priv,
const gchar* data)
{
gchar* decrypted_data = NULL;
guint i = 0;
GString *script;
gchar** inputs;
/* Handle case that user dont want to store password */
if (!strncmp (data, "never", 5))
return;
#if 0
if (!formhistory_check_master_password (NULL, priv))
return;
#endif
if (!(decrypted_data = formhistory_decrypt (data, priv->master_password)))
return;
script = g_string_new ("");
inputs = g_strsplit (decrypted_data, "|||", 0);
while (inputs[i] != NULL)
{
gchar** parts = g_strsplit (inputs[i], "|,|", 3);
if (parts && parts[0] && parts[1] && parts[2])
{
g_string_append_printf (script, "node = null;"
"node = document.getElementById ('%s');"
"if (!node) { node = document.getElementsByName ('%s')[0]; }"
"if (node && node.type == '%s') { node.value = '%s'; }",
parts[0], parts[0], parts[2], parts[1]);
}
g_strfreev (parts);
i++;
}
g_free (decrypted_data);
g_strfreev (inputs);
g_free (sokoke_js_script_eval (js_context, script->str, NULL));
g_string_free (script, TRUE);
}
static void
formhistory_frame_loaded_cb (WebKitWebView* web_view,
WebKitWebFrame* web_frame,
MidoriExtension* extension)
{
const gchar* page_uri;
const gchar* count_request;
FormHistoryPriv* priv;
JSContextRef js_context;
gchar* data;
gchar* domain;
gchar* count;
page_uri = webkit_web_frame_get_uri (web_frame);
if (!page_uri)
return;
count_request = "document.querySelectorAll('input[type=password]').length";
js_context = webkit_web_frame_get_global_context (web_frame);
count = sokoke_js_script_eval (js_context, count_request, NULL);
if (count && count[0] == '0')
{
g_free (count);
return;
}
g_free (count);
priv = g_object_get_data (G_OBJECT (extension), "priv");
domain = midori_uri_parse_hostname (webkit_web_frame_get_uri (web_frame), NULL);
data = formhistory_get_login_data (priv->db, domain);
g_free (domain);
if (!data)
return;
formhistory_fill_login_data (js_context, priv, data);
g_free (data);
}
#endif
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser);
static void
formhistory_add_tab_cb (MidoriBrowser* browser,
MidoriView* view,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_connect (web_view, "window-object-cleared",
G_CALLBACK (formhistory_window_object_cleared_cb), extension);
g_signal_connect (web_view, "navigation-policy-decision-requested",
G_CALLBACK (formhistory_navigation_decision_cb), extension);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
g_signal_connect (web_view, "onload-event",
G_CALLBACK (formhistory_frame_loaded_cb), extension);
#endif
}
static void
formhistory_add_tab_foreach_cb (MidoriView* view,
MidoriExtension* extension)
{
formhistory_add_tab_cb (NULL, view, extension);
}
static void
formhistory_app_add_browser_cb (MidoriApp* app,
MidoriBrowser* browser,
MidoriExtension* extension)
{
GtkAccelGroup* acg = gtk_accel_group_new ();
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action = gtk_action_new ("FormHistoryToggleState",
_("Toggle form history state"),
_("Activate or deactivate form history for the current tab."), NULL);
gtk_window_add_accel_group (GTK_WINDOW (browser), acg);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", extension);
g_signal_connect (action, "activate",
G_CALLBACK (formhistory_toggle_state_cb), browser);
gtk_action_group_add_action_with_accel (action_group, action, "<Ctrl><Shift>F");
gtk_action_set_accel_group (action, acg);
gtk_action_connect_accelerator (action);
if (midori_extension_get_boolean (extension, "always-load"))
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_add_tab_foreach_cb, extension);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
g_signal_connect (extension, "deactivate",
G_CALLBACK (formhistory_deactivate_cb), browser);
}
static void
formhistory_deactivate_tab (MidoriView* view,
MidoriExtension* extension)
{
GtkWidget* web_view = midori_view_get_web_view (view);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_window_object_cleared_cb, extension);
g_signal_handlers_disconnect_by_func (
web_view, formhistory_navigation_decision_cb, extension);
#if WEBKIT_CHECK_VERSION (1, 3, 8)
g_signal_handlers_disconnect_by_func (
web_view, formhistory_frame_loaded_cb, extension);
#endif
}
static void
formhistory_deactivate_cb (MidoriExtension* extension,
MidoriBrowser* browser)
{
MidoriApp* app = midori_extension_get_app (extension);
FormHistoryPriv* priv = g_object_get_data (G_OBJECT (extension), "priv");
GtkActionGroup* action_group = midori_browser_get_action_group (browser);
GtkAction* action;
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
g_signal_handlers_disconnect_by_func (
extension, formhistory_deactivate_cb, browser);
g_signal_handlers_disconnect_by_func (
app, formhistory_app_add_browser_cb, extension);
midori_browser_foreach (browser,
(GtkCallback)formhistory_deactivate_tab, extension);
g_object_set_data (G_OBJECT (browser), "FormHistoryExtension", NULL);
action = gtk_action_group_get_action (action_group, "FormHistoryToggleState");
if (action != NULL)
{
gtk_action_group_remove_action (action_group, action);
g_object_unref (action);
}
formhistory_private_destroy (priv);
}
static void
formhistory_activate_cb (MidoriExtension* extension,
MidoriApp* app)
{
const gchar* config_dir;
gchar* filename;
sqlite3* db;
char* errmsg = NULL, *errmsg2 = NULL;
KatzeArray* browsers;
MidoriBrowser* browser;
FormHistoryPriv* priv;
priv = formhistory_private_new ();
priv->master_password = NULL;
priv->master_password_canceled = 0;
formhistory_construct_popup_gui (priv);
config_dir = midori_extension_get_config_dir (extension);
katze_mkdir_with_parents (config_dir, 0700);
filename = g_build_filename (config_dir, "forms.db", NULL);
if (sqlite3_open (filename, &db) != SQLITE_OK)
{
/* If the folder is /, this is a test run, thus no error */
if (!g_str_equal (midori_extension_get_config_dir (extension), "/"))
g_warning (_("Failed to open database: %s\n"), sqlite3_errmsg (db));
sqlite3_close (db);
}
g_free (filename);
if ((sqlite3_exec (db, "CREATE TABLE IF NOT EXISTS "
"forms (domain text, field text, value text)",
NULL, NULL, &errmsg) == SQLITE_OK))
{
sqlite3_exec (db,
/* "PRAGMA synchronous = OFF; PRAGMA temp_store = MEMORY" */
"PRAGMA count_changes = OFF; PRAGMA journal_mode = TRUNCATE;",
NULL, NULL, &errmsg);
priv->db = db;
}
else
{
if (errmsg)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg);
sqlite3_free (errmsg);
if (errmsg2)
{
g_critical (_("Failed to execute database statement: %s\n"), errmsg2);
sqlite3_free (errmsg2);
}
}
sqlite3_close (db);
}
g_object_set_data (G_OBJECT (extension), "priv", priv);
browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
formhistory_app_add_browser_cb (app, browser, extension);
g_signal_connect (app, "add-browser",
G_CALLBACK (formhistory_app_add_browser_cb), extension);
g_object_unref (browsers);
}
static void
formhistory_preferences_response_cb (GtkWidget* dialog,
gint response_id,
MidoriExtension* extension)
{
GtkWidget* checkbox;
gboolean old_state;
gboolean new_state;
MidoriApp* app;
KatzeArray* browsers;
MidoriBrowser* browser;
if (response_id == GTK_RESPONSE_APPLY)
{
checkbox = g_object_get_data (G_OBJECT (dialog), "always-load-checkbox");
new_state = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
old_state = midori_extension_get_boolean (extension, "always-load");
if (old_state != new_state)
{
midori_extension_set_boolean (extension, "always-load", new_state);
app = midori_extension_get_app (extension);
browsers = katze_object_get_object (app, "browsers");
KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_deactivate_tab, extension);
g_signal_handlers_disconnect_by_func (
browser, formhistory_add_tab_cb, extension);
if (new_state)
{
midori_browser_foreach (browser,
(GtkCallback)formhistory_add_tab_foreach_cb, extension);
g_signal_connect (browser, "add-tab",
G_CALLBACK (formhistory_add_tab_cb), extension);
}
}
}
}
gtk_widget_destroy (dialog);
}
static void
formhistory_preferences_cb (MidoriExtension* extension)
{
GtkWidget* dialog;
GtkWidget* content_area;
GtkWidget* checkbox;
dialog = gtk_dialog_new ();
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
checkbox = gtk_check_button_new_with_label (_("Only activate form history via hotkey (Ctrl+Shift+F) per tab"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
!midori_extension_get_boolean (extension, "always-load"));
g_object_set_data (G_OBJECT (dialog), "always-load-checkbox", checkbox);
gtk_container_add (GTK_CONTAINER (content_area), checkbox);
/* FIXME: Add pref to disable password manager */
g_signal_connect (dialog,
"response",
G_CALLBACK (formhistory_preferences_response_cb),
extension);
gtk_widget_show_all (dialog);
}
static void
formhistory_toggle_state_cb (GtkAction* action,
MidoriBrowser* browser)
{
MidoriView* view = MIDORI_VIEW (midori_browser_get_current_tab (browser));
MidoriExtension* extension = g_object_get_data (G_OBJECT (browser), "FormHistoryExtension");
GtkWidget* web_view = midori_view_get_web_view (view);
if (g_signal_handler_find (web_view, G_SIGNAL_MATCH_FUNC,
g_signal_lookup ("window-object-cleared", MIDORI_TYPE_VIEW), 0, NULL,
formhistory_window_object_cleared_cb, extension))
{
formhistory_deactivate_tab (view, extension);
}
else
formhistory_add_tab_cb (browser, view, extension);
}
MidoriExtension*
extension_init (void)
{
MidoriExtension* extension;
extension = g_object_new (MIDORI_TYPE_EXTENSION,
"name", _("Form history filler"),
"description", _("Stores history of entered form data"),
"version", "2.0" MIDORI_VERSION_SUFFIX,
"authors", "Alexander V. Butenko <a.butenka@gmail.com>",
NULL);
midori_extension_install_boolean (extension, "always-load", TRUE);
g_signal_connect (extension, "activate",
G_CALLBACK (formhistory_activate_cb), NULL);
g_signal_connect (extension, "open-preferences",
G_CALLBACK (formhistory_preferences_cb), NULL);
return extension;
}

View file

@ -180,8 +180,6 @@ shortcuts_get_preferences_dialog (MidoriExtension* extension)
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
#endif
NULL);
g_signal_connect (dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed), &dialog);
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_PROPERTIES);
sokoke_widget_get_text_size (dialog, "M", &width, &height);
gtk_window_set_default_size (GTK_WINDOW (dialog), width * 52, height * 24);

View file

@ -66,11 +66,14 @@ statusbar_features_browser_notify_tab_cb (MidoriBrowser* browser,
GtkWidget* combobox)
{
MidoriView* view = MIDORI_VIEW (midori_browser_get_current_tab (browser));
gchar* zoom_level_text = g_strdup_printf ("%d%%",
(gint)(midori_view_get_zoom_level (view) * 100));
gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combobox))),
zoom_level_text);
g_free (zoom_level_text);
gchar* text;
if (view == NULL)
return;
text = g_strdup_printf ("%d%%", (gint)(midori_view_get_zoom_level (view) * 100));
gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combobox))), text);
g_free (text);
}
static void
@ -142,6 +145,8 @@ statusbar_features_app_add_browser_cb (MidoriApp* app,
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
if (midori_web_settings_has_plugin_support ())
{
button = katze_property_proxy (settings, "enable-plugins", "toggle");
g_object_set_data (G_OBJECT (button), "feature-label", _("Netscape plugins"));
image = gtk_image_new_from_stock (STOCK_PLUGINS, GTK_ICON_SIZE_MENU);
@ -151,6 +156,7 @@ statusbar_features_app_add_browser_cb (MidoriApp* app,
g_signal_connect (toolbar, "notify::toolbar-style",
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
}
button = katze_property_proxy (settings, "identify-as", "custom-user-agent");
gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 2);
button = gtk_combo_box_text_new_with_entry ();
@ -162,8 +168,9 @@ statusbar_features_app_add_browser_cb (MidoriApp* app,
G_CALLBACK (statusbar_features_zoom_level_changed_cb), browser);
g_signal_connect (browser, "notify::tab",
G_CALLBACK (statusbar_features_browser_notify_tab_cb), button);
statusbar_features_browser_notify_tab_cb (browser, NULL, button);
gtk_widget_show_all (bbox);
gtk_box_pack_start (GTK_BOX (statusbar), bbox, FALSE, FALSE, 3);
gtk_box_pack_end (GTK_BOX (statusbar), bbox, FALSE, FALSE, 3);
g_object_unref (statusbar);
g_signal_connect (extension, "deactivate",

View file

@ -394,11 +394,7 @@ static TBEditorWidget *tb_editor_create_dialog(MidoriBrowser *parent)
GTK_WINDOW(parent),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
#if !GTK_CHECK_VERSION(3,0,0)
vbox = (GTK_DIALOG(dialog))->vbox;
#else
vbox = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
#endif
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
gtk_box_set_spacing(GTK_BOX(vbox), 6);
gtk_widget_set_name(dialog, "GeanyDialog");
gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400);

View file

@ -27,9 +27,9 @@ sokoke_on_entry_focus_in_event (GtkEntry* entry,
g_object_get_data (G_OBJECT (entry), "sokoke_has_default"));
if (has_default)
{
gtk_entry_set_text (entry, "");
g_object_set_data (G_OBJECT (entry), "sokoke_has_default",
GINT_TO_POINTER (0));
gtk_entry_set_text (entry, "");
sokoke_widget_set_pango_font_style (GTK_WIDGET (entry),
PANGO_STYLE_NORMAL);
}
@ -46,9 +46,9 @@ sokoke_on_entry_focus_out_event (GtkEntry* entry,
{
const gchar* default_text = (const gchar*)g_object_get_data (
G_OBJECT (entry), "sokoke_default_text");
gtk_entry_set_text (entry, default_text);
g_object_set_data (G_OBJECT (entry),
"sokoke_has_default", GINT_TO_POINTER (1));
gtk_entry_set_text (entry, default_text);
sokoke_widget_set_pango_font_style (GTK_WIDGET (entry),
PANGO_STYLE_ITALIC);
}
@ -73,6 +73,9 @@ gtk_entry_set_placeholder_text (GtkEntry* entry,
/* Note: The default text initially overwrites any previous text */
gchar* old_value = g_object_get_data (G_OBJECT (entry),
"sokoke_default_text");
g_object_set_data (G_OBJECT (entry), "sokoke_default_text",
(gpointer)default_text);
if (!old_value)
{
g_object_set_data (G_OBJECT (entry), "sokoke_has_default",
@ -98,8 +101,12 @@ gtk_entry_set_placeholder_text (GtkEntry* entry,
PANGO_STYLE_ITALIC);
}
}
g_object_set_data (G_OBJECT (entry), "sokoke_default_text",
(gpointer)default_text);
}
const gchar*
gtk_entry_get_placeholder_text (GtkEntry* entry)
{
return g_object_get_data (G_OBJECT (entry), "sokoke_default_text");
}
#endif

View file

@ -6,7 +6,7 @@
G_BEGIN_DECLS
#if GTK_CHECK_VERSION (3, 2, 0)
#if GTK_CHECK_VERSION (3, 2, 0) && defined (GTK_DISABLE_DEPRECATED)
#define GTK_TYPE_VBOX GTK_TYPE_BOX
#define GtkVBox GtkBox
#define GtkVBoxClass GtkBoxClass
@ -89,8 +89,10 @@ G_BEGIN_DECLS
#if !GTK_CHECK_VERSION (3, 2, 0) && defined (HAVE_HILDON_2_2)
#define gtk_entry_set_placeholder_text hildon_gtk_entry_set_placeholder_text
#define gtk_entry_get_placeholder_text hildon_gtk_entry_get_placeholder_text
#elif !GTK_CHECK_VERSION (3, 2, 0)
#define gtk_entry_set_placeholder_text sokoke_entry_set_default_text
void gtk_entry_set_placeholder_text (GtkEntry* entry, const gchar* text);
const gchar* gtk_entry_get_placeholder_text (GtkEntry* entry);
#endif
#if !GTK_CHECK_VERSION(2, 12, 0)

View file

@ -74,6 +74,13 @@ GList* kalistglobal;
static void
katze_array_finalize (GObject* object);
static void
_katze_array_update (KatzeArray* array)
{
g_object_set_data (G_OBJECT (array), "last-update",
GINT_TO_POINTER (time (NULL)));
}
static void
_katze_array_add_item (KatzeArray* array,
gpointer item)
@ -84,6 +91,7 @@ _katze_array_add_item (KatzeArray* array,
katze_item_set_parent (item, array);
array->items = g_list_append (array->items, item);
_katze_array_update (array);
}
static void
@ -95,6 +103,7 @@ _katze_array_remove_item (KatzeArray* array,
if (KATZE_IS_ITEM (item))
katze_item_set_parent (item, NULL);
g_object_unref (item);
_katze_array_update (array);
}
static void
@ -104,6 +113,7 @@ _katze_array_move_item (KatzeArray* array,
{
array->items = g_list_remove (array->items, item);
array->items = g_list_insert (array->items, item, position);
_katze_array_update (array);
}
static void
@ -112,9 +122,10 @@ _katze_array_clear (KatzeArray* array)
GObject* item;
while ((item = g_list_nth_data (array->items, 0)))
katze_array_remove_item (array, item);
g_signal_emit (array, signals[REMOVE_ITEM], 0, item);
g_list_free (array->items);
array->items = NULL;
_katze_array_update (array);
}
static void
@ -192,7 +203,7 @@ katze_array_class_init (KatzeArrayClass* class)
"update",
G_TYPE_FROM_CLASS (class),
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
0,
G_STRUCT_OFFSET (KatzeArrayClass, update),
0,
NULL,
g_cclosure_marshal_VOID__VOID,
@ -205,6 +216,7 @@ katze_array_class_init (KatzeArrayClass* class)
class->remove_item = _katze_array_remove_item;
class->move_item = _katze_array_move_item;
class->clear = _katze_array_clear;
class->update = _katze_array_update;
}
static void
@ -217,14 +229,11 @@ katze_array_init (KatzeArray* array)
static void
katze_array_finalize (GObject* object)
{
KatzeArray* array;
guint i;
gpointer item;
KatzeArray* array = KATZE_ARRAY (object);
GList* items;
array = KATZE_ARRAY (object);
i = 0;
while ((item = g_list_nth_data (array->items, i++)))
g_object_unref (item);
for (items = array->items; items; items = g_list_next (items))
g_object_unref (items->data);
g_list_free (array->items);
G_OBJECT_CLASS (katze_array_parent_class)->finalize (object);
@ -364,37 +373,39 @@ katze_array_get_item_index (KatzeArray* array,
/**
* katze_array_find_token:
* @array: a #KatzeArray
* @token: a token string
* @token: a token string, or "token keywords" string
*
* Looks up an item in the array which has the specified token.
*
* This function will silently fail if the type of the list
* is not based on #GObject and only #KatzeItem children
* are checked for their token, any other objects are skipped.
* This function will fail if the type of the list
* is not based on #KatzeItem children.
*
* Note that @token is by definition unique to one item.
*
* Since 0.4.4 @token can be a "token keywords" string.
*
* Return value: an item, or %NULL
**/
gpointer
katze_array_find_token (KatzeArray* array,
const gchar* token)
{
guint i;
gpointer item;
goffset token_length;
GList* items;
g_return_val_if_fail (KATZE_IS_ARRAY (array), NULL);
g_return_val_if_fail (katze_array_is_a (array, KATZE_TYPE_ITEM), NULL);
g_return_val_if_fail (token != NULL, NULL);
i = 0;
while ((item = g_list_nth_data (array->items, i++)))
token_length = strchr (token, ' ') - token;
if (token_length < 1)
token_length = strlen (token);
for (items = array->items; items; items = g_list_next (items))
{
const gchar* found_token;
if (!KATZE_IS_ITEM (item))
continue;
found_token = ((KatzeItem*)item)->token;
if (!g_strcmp0 (found_token, token))
return item;
const gchar* found_token = ((KatzeItem*)items->data)->token;
if (found_token != NULL && !strncmp (token, found_token, token_length))
return items->data;
}
return NULL;
}
@ -406,9 +417,8 @@ katze_array_find_token (KatzeArray* array,
*
* Looks up an item in the array which has the specified URI.
*
* This function will silently fail if the type of the list
* is not based on #GObject and only #KatzeItem children
* are checked for their token, any other objects are skipped.
* This function will fail if the type of the list
* is not based on #KatzeItem children.
*
* Return value: an item, or %NULL
*
@ -418,19 +428,17 @@ gpointer
katze_array_find_uri (KatzeArray* array,
const gchar* uri)
{
guint i;
gpointer item;
GList* items;
i = 0;
while ((item = g_list_nth_data (array->items, i++)))
g_return_val_if_fail (KATZE_IS_ARRAY (array), NULL);
g_return_val_if_fail (katze_array_is_a (array, KATZE_TYPE_ITEM), NULL);
g_return_val_if_fail (uri != NULL, NULL);
for (items = array->items; items; items = g_list_next (items))
{
const gchar* found_uri;
if (!KATZE_IS_ITEM (item))
continue;
found_uri = ((KatzeItem*)item)->uri;
if (!g_strcmp0 (found_uri, uri))
return item;
const gchar* found_uri = ((KatzeItem*)items->data)->uri;
if (found_uri != NULL && !strcmp (found_uri, uri))
return items->data;
}
return NULL;
}

View file

@ -355,11 +355,16 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
gint summand;
KatzeItem* item;
GtkWidget* menuitem;
const gchar* icon_name;
GdkPixbuf* icon;
GtkWidget* image;
GtkWidget* submenu;
g_return_if_fail (KATZE_IS_ARRAY_ACTION (array_action));
g_return_if_fail (KATZE_IS_ITEM (array));
g_return_if_fail (GTK_IS_MENU_SHELL (menu));
g_return_if_fail (GTK_IS_TOOL_ITEM (proxy)
|| GTK_IS_MENU_ITEM (proxy)
|| GTK_IS_WINDOW (proxy));
if (!KATZE_IS_ARRAY (array))
return;
@ -385,18 +390,7 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
}
menuitem = katze_image_menu_item_new_ellipsized (
katze_item_get_name (item));
if ((icon_name = katze_item_get_icon (item)) && *icon_name)
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
else
{
if (KATZE_ITEM_IS_FOLDER (item))
icon = gtk_widget_render_icon (menuitem,
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
else
icon = katze_load_cached_icon (katze_item_get_uri (item), proxy);
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
}
image = katze_item_get_image (item);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
gtk_image_menu_item_set_always_show_image (
GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
@ -406,8 +400,13 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
{
submenu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
/* Make sure menu appears to contain items */
gtk_menu_shell_append (GTK_MENU_SHELL (submenu),
gtk_separator_menu_item_new ());
g_signal_connect (menuitem, "select",
G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
g_signal_connect (menuitem, "activate",
G_CALLBACK (katze_array_action_menu_item_select_cb), array_action);
}
else
{
@ -421,21 +420,39 @@ katze_array_action_generate_menu (KatzeArrayAction* array_action,
}
}
static void
katze_array_action_menu_item_select_cb (GtkWidget* proxy,
KatzeArrayAction* array_action)
static gboolean
katze_array_action_menu_item_need_update (KatzeArrayAction* array_action,
GtkWidget* proxy)
{
GtkWidget* menu;
KatzeArray* array;
gint last_array_update, last_proxy_update;
gboolean handled;
array = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
/* last-update is set on all arrays; consider public API */
last_array_update = GPOINTER_TO_INT (
g_object_get_data (G_OBJECT (array), "last-update"));
last_proxy_update = GPOINTER_TO_INT (
g_object_get_data (G_OBJECT (proxy), "last-update"));
if (last_proxy_update > last_array_update)
return FALSE;
menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy));
gtk_container_foreach (GTK_CONTAINER (menu),
(GtkCallback)(gtk_widget_destroy), NULL);
array = g_object_get_data (G_OBJECT (proxy), "KatzeItem");
katze_array_action_generate_menu (array_action, array, GTK_MENU_SHELL (menu), proxy);
g_signal_emit (array_action, signals[POPULATE_FOLDER], 0, menu, array, &handled);
g_object_set_data (G_OBJECT (proxy), "last-update",
GINT_TO_POINTER (time (NULL)));
return TRUE;
}
static void
katze_array_action_menu_item_select_cb (GtkWidget* proxy,
KatzeArrayAction* array_action)
{
katze_array_action_menu_item_need_update (array_action, proxy);
}
static void
@ -458,20 +475,21 @@ katze_array_action_proxy_clicked_cb (GtkWidget* proxy,
KatzeArray* array;
gboolean handled = FALSE;
array = (KatzeArray*)g_object_get_data (G_OBJECT (proxy), "KatzeItem");
if (GTK_IS_MENU_ITEM (proxy))
{
g_object_set_data (G_OBJECT (proxy), "KatzeItem", array_action->array);
katze_array_action_menu_item_select_cb (proxy, array_action);
if (katze_array_action_menu_item_need_update (array_action, proxy))
{
g_signal_emit (array_action, signals[POPULATE_FOLDER], 0,
gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)),
array_action->array, &handled);
array, &handled);
if (!handled)
g_signal_emit (array_action, signals[POPULATE_POPUP], 0,
gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)));
}
return;
}
array = (KatzeArray*)g_object_get_data (G_OBJECT (proxy), "KatzeArray");
if (KATZE_IS_ITEM (array) && katze_item_get_uri ((KatzeItem*)array))
{
katze_array_action_activate_item (array_action, KATZE_ITEM (array), 1);
@ -552,7 +570,6 @@ katze_array_action_item_notify_cb (KatzeItem* item,
const gchar* property;
const gchar* title;
const gchar* desc;
GdkPixbuf* icon;
GtkWidget* image;
if (!G_IS_PARAM_SPEC_STRING (pspec))
@ -579,16 +596,13 @@ katze_array_action_item_notify_cb (KatzeItem* item,
}
else if (KATZE_ITEM_IS_BOOKMARK (item) && !strcmp (property, "uri"))
{
icon = katze_load_cached_icon (katze_item_get_uri (item), GTK_WIDGET (toolitem));
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
image = katze_item_get_image (item);
gtk_widget_show (image);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image);
}
else if (!strcmp (property, "icon"))
{
image = gtk_image_new_from_icon_name (katze_item_get_icon (item),
GTK_ICON_SIZE_MENU);
image = katze_item_get_image (item);
gtk_widget_show (image);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image);
}
@ -600,25 +614,12 @@ katze_array_action_proxy_create_menu_proxy_cb (GtkWidget* proxy,
{
KatzeArrayAction* array_action;
GtkWidget* menuitem;
const gchar* icon_name;
GtkWidget* image;
GdkPixbuf* icon;
array_action = g_object_get_data (G_OBJECT (proxy), "KatzeArrayAction");
menuitem = katze_image_menu_item_new_ellipsized (
katze_item_get_name (item));
if ((icon_name = katze_item_get_icon (item)) && *icon_name)
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
else
{
if (KATZE_ITEM_IS_FOLDER (item))
icon = gtk_widget_render_icon (menuitem,
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
else
icon = katze_load_cached_icon (katze_item_get_uri (item), proxy);
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
}
image = katze_item_get_image (item);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), image);
gtk_image_menu_item_set_always_show_image (
GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
@ -674,7 +675,6 @@ katze_array_action_create_tool_item_for (KatzeArrayAction* array_action,
const gchar* uri;
const gchar* desc;
GtkToolItem* toolitem;
GdkPixbuf* icon;
GtkWidget* image;
GtkWidget* label;
@ -686,20 +686,12 @@ katze_array_action_create_tool_item_for (KatzeArrayAction* array_action,
return gtk_separator_tool_item_new ();
if (KATZE_ITEM_IS_FOLDER (item))
{
toolitem = gtk_toggle_tool_button_new ();
icon = gtk_widget_render_icon (GTK_WIDGET (toolitem),
GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
}
else
{
toolitem = gtk_tool_button_new (NULL, "");
icon = katze_load_cached_icon (uri, GTK_WIDGET (toolitem));
}
g_signal_connect (toolitem, "create-menu-proxy",
G_CALLBACK (katze_array_action_proxy_create_menu_proxy_cb), item);
image = gtk_image_new_from_pixbuf (icon);
g_object_unref (icon);
image = katze_item_get_image (item);
gtk_widget_show (image);
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toolitem), image);
label = gtk_label_new (NULL);
@ -739,6 +731,9 @@ static void
katze_array_action_connect_proxy (GtkAction* action,
GtkWidget* proxy)
{
KatzeArrayAction* array_action = KATZE_ARRAY_ACTION (action);
g_object_set_data (G_OBJECT (proxy), "KatzeItem", array_action->array);
GTK_ACTION_CLASS (katze_array_action_parent_class)->connect_proxy (
action, proxy);
@ -750,9 +745,10 @@ katze_array_action_connect_proxy (GtkAction* action,
else if (GTK_IS_MENU_ITEM (proxy))
{
gtk_menu_item_set_submenu (GTK_MENU_ITEM (proxy), gtk_menu_new ());
/* FIXME: 'select' doesn't cover all ways of selection */
g_signal_connect (proxy, "select",
G_CALLBACK (katze_array_action_proxy_clicked_cb), action);
g_signal_connect (proxy, "activate",
G_CALLBACK (katze_array_action_proxy_clicked_cb), action);
}
}

View file

@ -27,7 +27,7 @@
#include <sqlite3.h>
#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"
#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
#define CREATE_TABLE "CREATE TABLE IF NOT EXISTS moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
@ -71,80 +71,6 @@ G_DEFINE_TYPE_WITH_CODE (KatzeHttpCookiesSqlite, katze_http_cookies_sqlite, G_TY
Copyright (C) 2009 Collabora Ltd.
Mostly copied from libSoup 2.30, coding style retained */
static void
try_create_table (sqlite3 *db)
{
char *error = NULL;
if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) {
g_warning ("Failed to execute query: %s", error);
sqlite3_free (error);
}
}
static void
exec_query_with_try_create_table (sqlite3* db,
const char* sql,
int (*callback)(void*,int,char**,char**),
void *argument)
{
char *error = NULL;
gboolean try_create = TRUE;
try_exec:
if (sqlite3_exec (db, sql, callback, argument, &error)) {
if (try_create) {
try_create = FALSE;
try_create_table (db);
sqlite3_free (error);
error = NULL;
goto try_exec;
} else {
g_warning ("Failed to execute query: %s", error);
sqlite3_free (error);
}
}
}
static int
callback (void *data, int argc, char **argv, char **colname)
{
SoupCookie *cookie = NULL;
SoupCookieJar *jar = SOUP_COOKIE_JAR (data);
char *name, *value, *host, *path;
gint64 expire_time;
time_t now;
int max_age;
gboolean http_only = FALSE, secure = FALSE;
now = time (NULL);
name = argv[COL_NAME];
value = argv[COL_VALUE];
host = argv[COL_HOST];
path = argv[COL_PATH];
expire_time = g_ascii_strtoull (argv[COL_EXPIRY], NULL, 10);
if (now >= expire_time)
return 0;
max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
secure = (g_strcmp0 (argv[COL_SECURE], "1") == 0);
cookie = soup_cookie_new (name, value, host, path, max_age);
if (secure)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
soup_cookie_set_http_only (cookie, TRUE);
soup_cookie_jar_add_cookie (jar, cookie);
return 0;
}
/* Follows sqlite3 convention; returns TRUE on error */
static gboolean
katze_http_cookies_sqlite_open_db (KatzeHttpCookiesSqlite* http_cookies)
@ -157,23 +83,91 @@ katze_http_cookies_sqlite_open_db (KatzeHttpCookiesSqlite* http_cookies)
return TRUE;
}
if (sqlite3_exec (http_cookies->db, "PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;", NULL, NULL, &error)) {
if (sqlite3_exec (http_cookies->db, CREATE_TABLE, NULL, NULL, &error)) {
g_warning ("Failed to execute query: %s", error);
sqlite3_free (error);
}
if (sqlite3_exec (http_cookies->db, "PRAGMA secure_delete = 1;",
NULL, NULL, &error)) {
g_warning ("Failed to execute query: %s", error);
sqlite3_free (error);
}
sqlite3_exec (http_cookies->db,
/* Arguably cookies are like a cache, so performance over integrity */
"PRAGMA synchronous = OFF; PRAGMA temp_store = MEMORY;"
"PRAGMA count_changes = OFF; PRAGMA journal_mode = TRUNCATE;",
NULL, NULL, &error);
return FALSE;
}
static void
katze_http_cookies_sqlite_load (KatzeHttpCookiesSqlite* http_cookies)
{
const char *name, *value, *host, *path;
sqlite3_stmt* stmt;
SoupCookie *cookie = NULL;
gint64 expire_time;
time_t now;
int max_age;
gboolean http_only = FALSE, secure = FALSE;
char *query;
int result;
if (http_cookies->db == NULL) {
if (katze_http_cookies_sqlite_open_db (http_cookies))
return;
}
exec_query_with_try_create_table (http_cookies->db, QUERY_ALL, callback, http_cookies->jar);
sqlite3_prepare_v2 (http_cookies->db, QUERY_ALL, strlen (QUERY_ALL) + 1, &stmt, NULL);
result = sqlite3_step (stmt);
if (result != SQLITE_ROW)
{
if (result == SQLITE_ERROR)
g_print (_("Failed to load cookies\n"));
sqlite3_reset (stmt);
return;
}
while (result == SQLITE_ROW)
{
now = time (NULL);
name = (const char*)sqlite3_column_text (stmt, COL_NAME);
value = (const char*)sqlite3_column_text (stmt, COL_VALUE);
host = (const char*)sqlite3_column_text (stmt, COL_HOST);
path = (const char*)sqlite3_column_text (stmt, COL_PATH);
expire_time = sqlite3_column_int64 (stmt,COL_EXPIRY);
secure = sqlite3_column_int (stmt, COL_SECURE);
http_only = sqlite3_column_int (stmt, COL_HTTP_ONLY);
if (now >= expire_time)
{
/* Cookie expired, remove it from database */
query = sqlite3_mprintf (QUERY_DELETE, name, host);
sqlite3_exec (http_cookies->db, QUERY_DELETE, NULL, NULL, NULL);
sqlite3_free (query);
result = sqlite3_step (stmt);
continue;
}
max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
cookie = soup_cookie_new (name, value, host, path, max_age);
if (secure)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
soup_cookie_set_http_only (cookie, TRUE);
soup_cookie_jar_add_cookie (http_cookies->jar, cookie);
result = sqlite3_step (stmt);
}
if (stmt)
{
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
}
}
static void
katze_http_cookies_sqlite_jar_changed_cb (SoupCookieJar* jar,
@ -220,7 +214,7 @@ katze_http_cookies_sqlite_jar_changed_cb (SoupCookieJar* jar,
query = sqlite3_mprintf (QUERY_DELETE,
old_cookie->name,
old_cookie->domain);
exec_query_with_try_create_table (http_cookies->db, query, NULL, NULL);
sqlite3_exec (http_cookies->db, query, NULL, NULL, NULL);
sqlite3_free (query);
}
@ -234,7 +228,7 @@ katze_http_cookies_sqlite_jar_changed_cb (SoupCookieJar* jar,
expires,
new_cookie->secure,
new_cookie->http_only);
exec_query_with_try_create_table (http_cookies->db, query, NULL, NULL);
sqlite3_exec (http_cookies->db, query, NULL, NULL, NULL);
sqlite3_free (query);
}
}

View file

@ -315,6 +315,8 @@ katze_item_set_name (KatzeItem* item,
g_return_if_fail (KATZE_IS_ITEM (item));
katze_assign (item->name, g_strdup (name));
if (item->parent)
katze_array_update ((KatzeArray*)item->parent);
g_object_notify (G_OBJECT (item), "name");
}
@ -414,9 +416,51 @@ katze_item_set_icon (KatzeItem* item,
g_return_if_fail (KATZE_IS_ITEM (item));
katze_item_set_meta_string (item, "icon", icon);
if (item->parent)
katze_array_update ((KatzeArray*)item->parent);
g_object_notify (G_OBJECT (item), "icon");
}
/**
* katze_item_get_image:
* @item: a #KatzeItem
*
* Retrieves a #GtkImage fit to display @item.
*
* Return value: the icon of the item
*
* Since: 0.4.4
**/
GtkWidget*
katze_item_get_image (KatzeItem* item)
{
GtkWidget* image;
GdkPixbuf* pixbuf;
const gchar* icon;
g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
if (KATZE_ITEM_IS_FOLDER (item))
image = gtk_image_new_from_stock (GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
else if ((pixbuf = g_object_get_data (G_OBJECT (item), "pixbuf")))
image = gtk_image_new_from_pixbuf (pixbuf);
else if ((icon = katze_item_get_icon (item)) && !strchr (icon, '/'))
image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
else
{
if (!(icon && (pixbuf = katze_load_cached_icon (icon, NULL))))
pixbuf = katze_load_cached_icon (item->uri, NULL);
if (pixbuf)
{
image = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
}
else
image = gtk_image_new_from_stock (GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
}
return image;
}
/**
* katze_item_get_token:
* @item: a #KatzeItem
@ -527,17 +571,22 @@ katze_item_set_meta_data_value (KatzeItem* item,
* Return value: a string, or %NULL
*
* Since: 0.1.8
*
* Since 0.4.4 "" is treated like %NULL.
**/
const gchar*
katze_item_get_meta_string (KatzeItem* item,
const gchar* key)
{
const gchar* value;
g_return_val_if_fail (KATZE_IS_ITEM (item), NULL);
g_return_val_if_fail (key != NULL, NULL);
if (g_str_has_prefix (key, "midori:"))
key = &key[7];
return g_hash_table_lookup (item->metadata, key);
value = g_hash_table_lookup (item->metadata, key);
return value && *value ? value : NULL;
}
/**

View file

@ -12,7 +12,7 @@
#ifndef __KATZE_ITEM_H__
#define __KATZE_ITEM_H__
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
@ -91,6 +91,9 @@ void
katze_item_set_icon (KatzeItem* item,
const gchar* icon);
GtkWidget*
katze_item_get_image (KatzeItem* item);
const gchar*
katze_item_get_token (KatzeItem* item);

View file

@ -97,8 +97,8 @@ katze_throbber_realize (GtkWidget* widget);
#if GTK_CHECK_VERSION (3, 0, 0)
static void
katze_throbber_get_preferred_height (GtkWidget *widget,
gint *minimal_width,
gint *natural_width);
gint *minimal_height,
gint *natural_height);
static void
katze_throbber_get_preferred_width (GtkWidget *widget,
gint *minimal_width,
@ -495,8 +495,8 @@ katze_throbber_set_animated (KatzeThrobber* throbber,
(GSourceFunc)katze_throbber_timeout,
throbber,
(GDestroyNotify)katze_throbber_timeout_destroy);
gtk_widget_queue_draw (GTK_WIDGET (throbber));
#endif
gtk_widget_queue_draw (GTK_WIDGET (throbber));
g_object_notify (G_OBJECT (throbber), "animated");
}
@ -857,14 +857,14 @@ katze_throbber_size_request (GtkWidget* widget,
#if GTK_CHECK_VERSION (3, 0, 0)
static void
katze_throbber_get_preferred_height (GtkWidget *widget,
gint *minimal_width,
gint *natural_width)
gint *minimal_height,
gint *natural_height)
{
GtkRequisition requisition;
katze_throbber_size_request (widget, &requisition);
*minimal_width = *natural_width = requisition.height;
*minimal_height = *natural_height = requisition.height;
}
static void
@ -902,6 +902,7 @@ katze_throbber_aligned_coords (GtkWidget* widget,
#endif
#if GTK_CHECK_VERSION (3, 0, 0)
allocation.x = allocation.y = 0;
allocation.width = gtk_widget_get_allocated_width (widget);
allocation.height = gtk_widget_get_allocated_height (widget);
gtk_widget_get_preferred_size (widget, &requisition, NULL);

View file

@ -910,21 +910,29 @@ katze_widget_popup_position_menu (GtkMenu* menu,
GtkRequisition widget_req;
KatzePopupInfo* info = user_data;
GtkWidget* widget = info->widget;
GdkWindow* window = gtk_widget_get_window (widget);
gint widget_height;
gtk_widget_get_allocation (widget, &allocation);
if (!window)
return;
#if !GTK_CHECK_VERSION (3, 0, 0)
if (GTK_IS_ENTRY (widget))
window = gdk_window_get_parent (window);
#endif
/* Retrieve size and position of both widget and menu */
if (!gtk_widget_get_has_window (widget))
{
gdk_window_get_position (gtk_widget_get_window (widget), &wx, &wy);
gtk_widget_get_allocation (widget, &allocation);
gdk_window_get_origin (window, &wx, &wy);
wx += allocation.x;
wy += allocation.y;
}
else
gdk_window_get_origin (gtk_widget_get_window (widget), &wx, &wy);
#if GTK_CHECK_VERSION (3, 0, 0)
gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_req, NULL);
gtk_widget_get_preferred_size (widget, &widget_req, NULL);
#else
gtk_widget_size_request (GTK_WIDGET (menu), &menu_req);
gtk_widget_size_request (widget, &widget_req);
#endif
menu_width = menu_req.width;
widget_height = widget_req.height; /* Better than allocation.height */
@ -1190,6 +1198,16 @@ katze_strip_mnemonics (const gchar* original)
return result;
}
const gchar*
katze_skip_whitespace (const gchar* str)
{
if (str == NULL)
return NULL;
while (*str == ' ' || *str == '\t' || *str == '\n')
str++;
return str;
}
/**
* katze_object_has_property:
* @object: a #GObject
@ -1520,6 +1538,8 @@ katze_uri_entry_changed_cb (GtkWidget* entry,
{
const gchar* uri = gtk_entry_get_text (GTK_ENTRY (entry));
gboolean valid = midori_uri_is_location (uri);
if (!valid && g_object_get_data (G_OBJECT (entry), "allow_%s"))
valid = uri && g_str_has_prefix (uri, "%s");
if (*uri && !valid)
{
GdkColor bg_color = { 0 };

View file

@ -58,6 +58,20 @@ G_BEGIN_DECLS
**/
#define katze_strv_assign(lvalue, rvalue) lvalue = (g_strfreev (lvalue), rvalue)
/**
* katze_str_non_null:
* @str: a string, or %NULL
*
* Returns "" if @str is %NULL.
*
* Since: 0.4.4
**/
static inline const gchar*
katze_str_non_null (const gchar* str)
{
return str ? str : "";
}
GtkWidget*
katze_property_proxy (gpointer object,
const gchar* property,
@ -101,6 +115,9 @@ katze_bookmark_populate_tree_view (KatzeArray* array,
gchar*
katze_strip_mnemonics (const gchar* original);
const gchar*
katze_skip_whitespace (const gchar* str);
gboolean
katze_object_has_property (gpointer object,
const gchar* property);

View file

@ -85,6 +85,9 @@ namespace Midori {
if (uri == null)
return keywords;
string escaped = GLib.Uri.escape_string (keywords, ":/", true);
/* Allow DuckDuckGo to distinguish Midori and in turn share revenue */
if (uri == "https://duckduckgo.com/?q=%s")
return "https://duckduckgo.com/?q=%s&t=midori".printf (escaped);
if (uri.str ("%s") != null)
return uri.printf (escaped);
return uri + escaped;
@ -122,8 +125,17 @@ namespace Midori {
FIXME: Schemes are not handled
hostname_is_ip_address () is not used because
we'd have to separate the path from the URI first. */
return uri != null && uri[0].isdigit ()
&& (uri.chr (4, '.') != null || uri.chr (4, ':') != null);
if (uri == null)
return false;
/* IPv4 */
if (uri[0].isdigit () && (uri.chr (4, '.') != null))
return true;
/* IPv6 */
if (uri[0].isalnum () && uri[1].isalnum ()
&& uri[2].isalnum () && uri[3].isalnum () && uri[4] == ':'
&& (uri[5] == ':' || uri[5].isalnum ()))
return true;
return false;
}
public static bool is_valid (string? uri) {
return uri != null

View file

@ -441,7 +441,10 @@ midori_history_initialize (KatzeArray* array,
return FALSE;
}
sqlite3_exec (db, "PRAGMA journal_mode = TRUNCATE;", NULL, NULL, errmsg);
sqlite3_exec (db,
/* "PRAGMA synchronous = OFF; PRAGMA temp_store = MEMORY" */
"PRAGMA count_changes = OFF; PRAGMA journal_mode = TRUNCATE;",
NULL, NULL, errmsg);
if (*errmsg)
{
g_warning ("Failed to set journal mode: %s", *errmsg);
@ -528,14 +531,14 @@ midori_bookmarks_remove_item_cb (KatzeArray* array,
"DELETE FROM bookmarks WHERE uri = '%q' "
" AND folder = '%q'",
katze_item_get_uri (item),
katze_item_get_meta_string (item, "folder"));
katze_str_non_null (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"));
katze_str_non_null (katze_item_get_meta_string (item, "folder")));
if (sqlite3_exec (db, sqlcmd, NULL, NULL, &errmsg) != SQLITE_OK)
{
@ -958,18 +961,21 @@ midori_soup_session_settings_accept_language_cb (SoupSession* session,
if (referer && destination && !strstr (referer, destination->host))
{
SoupURI* stripped_uri = soup_uri_new (referer);
if (stripped_uri != NULL)
{
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");
g_message ("Referer %s stripped to %s", referer, stripped_referer);
soup_message_headers_replace (msg->request_headers, "Referer",
stripped_referer);
g_free (stripped_referer);
}
}
}
}
static void
@ -1029,6 +1035,7 @@ midori_load_soup_session (gpointer settings)
NULL);
#endif
g_object_set_data (G_OBJECT (session), "midori-settings", settings);
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);
@ -1474,21 +1481,6 @@ snapshot_load_finished_cb (GtkWidget* web_view,
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,
@ -1521,9 +1513,7 @@ 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:"))
if (g_str_has_prefix(uri, "javascript:"))
return NULL;
else if (g_file_test (uri, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
{
@ -1729,31 +1719,24 @@ midori_setup_inactivity_reset (MidoriBrowser* browser,
}
}
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 ();
MidoriWebSettings* settings = g_object_get_data (G_OBJECT (session), "midori-settings");
SoupSessionFeature* jar = soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
GSList* cookies = soup_cookie_jar_all_cookies (SOUP_COOKIE_JAR (jar));
SoupSessionFeature* feature;
gchar* cache;
/* HTTP Cookies/ Web Cookies */
for (; cookies != NULL; cookies = g_slist_next (cookies))
{
SoupCookie* cookie = cookies->data;
soup_cookie_jar_delete_cookie ((SoupCookieJar*)jar, cookie);
const gchar* domain = ((SoupCookie*)cookies->data)->domain;
if (midori_web_settings_get_site_data_policy (settings, domain)
== MIDORI_SITE_DATA_PRESERVE)
continue;
soup_cookie_jar_delete_cookie ((SoupCookieJar*)jar, cookies->data);
}
soup_cookies_free (cookies);
/* Removing KatzeHttpCookies makes it save outstanding changes */
@ -1764,18 +1747,36 @@ midori_clear_web_cookies_cb (void)
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);
/* Local shared objects/ Flash cookies */
if (midori_web_settings_has_plugin_support ())
{
#ifdef GDK_WINDOWING_X11
cache = g_build_filename (g_get_home_dir (), ".macromedia", "Flash_Player", NULL);
sokoke_remove_path (cache, TRUE);
g_free (cache);
#elif defined(GDK_WINDOWING_WIN32)
cache = g_build_filename (g_get_user_data_dir (), "Macromedia", "Flash Player", NULL);
sokoke_remove_path (cache, TRUE);
g_free (cache);
#elif defined(GDK_WINDOWING_QUARTZ)
cache = g_build_filename (g_get_home_dir (), "Library", "Preferences",
"Macromedia", "Flash Player", NULL);
sokoke_remove_path (cache, TRUE);
g_free (cache);
#endif
}
/* HTML5 databases */
webkit_remove_all_web_databases ();
/* HTML5 offline application caches */
#if WEBKIT_CHECK_VERSION (1, 3, 13)
/* Changing the size implies clearing the cache */
webkit_application_cache_set_maximum_size (
webkit_application_cache_get_maximum_size () - 1);
#endif
}
#endif
static void
midori_clear_saved_logins_cb (void)
@ -1794,35 +1795,33 @@ midori_clear_saved_logins_cb (void)
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);
gchar* cache = 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);
sokoke_remove_path (cache, TRUE);
g_free (cache);
}
#endif
#if WEBKIT_CHECK_VERSION (1, 3, 13)
static void
midori_clear_offline_appcache_cb (void)
midori_clear_page_icons_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);
gchar* cache = g_build_filename (g_get_user_cache_dir (),
PACKAGE_NAME, "icons", NULL);
/* FIXME: Exclude search engine icons */
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);
}
#endif
static void
midori_log_to_file (const gchar* log_domain,
@ -2009,6 +2008,9 @@ main (int argc,
else
g_set_application_name (_("Midori"));
/* Versioned prgname to override menuproxy blacklist */
g_set_prgname (PACKAGE_NAME "4");
if (version)
{
g_print (
@ -2104,34 +2106,24 @@ main (int argc,
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"),
sokoke_register_privacy_item ("web-cookies", _("Cookies and Website data"),
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)
/* TODO: Preserve page icons of search engines and merge privacy items */
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
sokoke_register_privacy_item ("page-icons", _("Website icons"),
G_CALLBACK (midori_clear_page_icons_cb));
/* 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 ()),
@ -2283,6 +2275,10 @@ main (int argc,
}
}
/* Informative text for private browsing unless we have a URI */
if (private && webapp == NULL && uris == NULL)
midori_browser_add_uri (browser, "about:private");
if (midori_browser_get_current_uri (browser) == NULL)
midori_browser_add_uri (browser, "about:blank");

View file

@ -69,7 +69,6 @@ struct _MidoriApp
GObject parent_instance;
MidoriBrowser* browser;
GtkAccelGroup* accel_group;
gchar* name;
MidoriWebSettings* settings;
@ -216,7 +215,6 @@ _midori_app_add_browser (MidoriApp* app,
g_return_if_fail (MIDORI_IS_APP (app));
g_return_if_fail (MIDORI_IS_BROWSER (browser));
gtk_window_add_accel_group (GTK_WINDOW (browser), app->accel_group);
g_object_connect (browser,
"signal::focus-in-event", midori_browser_focus_in_event_cb, app,
"signal::new-window", midori_browser_new_window_cb, app,
@ -722,12 +720,16 @@ midori_app_create_instance (MidoriApp* app)
if (!app->name)
{
#if HAVE_UNIQUE
const gchar* config = sokoke_set_config_dir (NULL);
gchar* name_hash;
name_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, config, -1);
app->name = g_strconcat ("midori", "_", name_hash, NULL);
g_free (name_hash);
g_object_notify (G_OBJECT (app), "name");
#else
app->name = g_strdup (PACKAGE_NAME);
#endif
}
if (!(display = gdk_display_get_default ()))
@ -767,8 +769,6 @@ midori_app_create_instance (MidoriApp* app)
static void
midori_app_init (MidoriApp* app)
{
app->accel_group = gtk_accel_group_new ();
app->settings = NULL;
app->bookmarks = NULL;
app->trash = NULL;
@ -793,8 +793,6 @@ midori_app_finalize (GObject* object)
{
MidoriApp* app = MIDORI_APP (object);
g_object_unref (app->accel_group);
katze_assign (app->name, NULL);
katze_object_assign (app->settings, NULL);
katze_object_assign (app->bookmarks, NULL);
@ -1351,7 +1349,9 @@ midori_app_setup (gchar** argument_vector)
/* libSoup uses threads, therefore if WebKit is built with libSoup
* or Midori is using it, we need to initialize threads. */
#if !GLIB_CHECK_VERSION (2, 32, 0)
if (!g_thread_supported ()) g_thread_init (NULL);
#endif
#if ENABLE_NLS
setlocale (LC_ALL, "");

View file

@ -1067,28 +1067,32 @@ katze_array_from_sqlite (sqlite3* db,
}
/**
* midori_array_query:
* midori_array_query_recursive:
* @array: the main bookmark array
* @fields: comma separated list of fields
* @condition: condition, like "folder = '%q'"
* @value: a value to be inserted if @condition contains %q
* @recursive: if %TRUE include children
*
* Stores the result in a #KatzeArray.
*
* Return value: a #KatzeArray on success, %NULL otherwise
*
* Since: 0.4.3
* Since: 0.4.4
**/
KatzeArray*
midori_array_query (KatzeArray* bookmarks,
midori_array_query_recursive (KatzeArray* bookmarks,
const gchar* fields,
const gchar* condition,
const gchar* value)
const gchar* value,
gboolean recursive)
{
sqlite3* db;
gchar* sqlcmd;
char* sqlcmd_value;
KatzeArray* array;
KatzeItem* item;
GList* list;
g_return_val_if_fail (KATZE_IS_ARRAY (bookmarks), NULL);
g_return_val_if_fail (fields, NULL);
@ -1108,6 +1112,45 @@ midori_array_query (KatzeArray* bookmarks,
else
array = katze_array_from_sqlite (db, sqlcmd);
g_free (sqlcmd);
if (!recursive)
return array;
KATZE_ARRAY_FOREACH_ITEM_L (item, array, list)
{
if (KATZE_ITEM_IS_FOLDER (item))
{
KatzeArray* subarray = midori_array_query_recursive (bookmarks,
fields, "folder='%q'", item->name, TRUE);
katze_item_set_name (KATZE_ITEM (subarray), item->name);
katze_array_add_item (array, subarray);
}
}
g_list_free (list);
return array;
}
/**
* midori_array_query:
* @array: the main bookmark array
* @fields: comma separated list of fields
* @condition: condition, like "folder = '%q'"
* @value: a value to be inserted if @condition contains %q
*
* Stores the result in a #KatzeArray.
*
* Return value: a #KatzeArray on success, %NULL otherwise
*
* Since: 0.4.3
*
* Deprecated: 0.4.4: Use midori_array_query_recursive() instead.
**/
KatzeArray*
midori_array_query (KatzeArray* bookmarks,
const gchar* fields,
const gchar* condition,
const gchar* value)
{
return midori_array_query_recursive (bookmarks, fields, condition, value, FALSE);
}

View file

@ -36,6 +36,13 @@ midori_array_query (KatzeArray* array,
const gchar* condition,
const gchar* value);
KatzeArray*
midori_array_query_recursive (KatzeArray* array,
const gchar* fields,
const gchar* condition,
const gchar* value,
gboolean recursive);
KatzeArray*
katze_array_from_sqlite (sqlite3* db,
const gchar* sqlcmd);

View file

@ -37,10 +37,6 @@
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_HILDON_2_2
#include <dbus/dbus.h>
#include <mce/mode-names.h>
@ -65,7 +61,6 @@ struct _MidoriBrowser
GtkActionGroup* action_group;
GtkWidget* menubar;
GtkWidget* menu_tools;
GtkWidget* throbber;
GtkWidget* navigationbar;
GtkWidget* bookmarkbar;
@ -177,11 +172,6 @@ midori_bookmarks_import_array_db (sqlite3* db,
KatzeArray* array,
gchar* folder);
void
midori_bookmarks_export_array_db (sqlite3* db,
KatzeArray* array,
const gchar* folder);
void
midori_browser_open_bookmark (MidoriBrowser* browser,
KatzeItem* item);
@ -303,8 +293,7 @@ _midori_browser_update_interface (MidoriBrowser* browser)
_action_set_sensitive (browser, "Next",
midori_view_get_next_page (view) != NULL);
gtk_action_set_visible (_action_by_name (browser, "AddSpeedDial"),
!midori_view_is_blank (view));
_action_set_visible (browser, "AddSpeedDial", !midori_view_is_blank (view));
_action_set_sensitive (browser, "SaveAs", midori_view_can_save (view));
_action_set_sensitive (browser, "Print", midori_view_can_print (view));
_action_set_sensitive (browser, "ZoomIn", midori_view_can_zoom_in (view));
@ -355,13 +344,13 @@ _midori_browser_update_interface (MidoriBrowser* browser)
{
midori_location_action_set_secondary_icon (
MIDORI_LOCATION_ACTION (action), STOCK_NEWS_FEED);
gtk_action_set_sensitive (_action_by_name (browser, "AddNewsFeed"), TRUE);
_action_set_sensitive (browser, "AddNewsFeed", TRUE);
}
else
{
midori_location_action_set_secondary_icon (
MIDORI_LOCATION_ACTION (action), GTK_STOCK_JUMP_TO);
gtk_action_set_sensitive (_action_by_name (browser, "AddNewsFeed"), FALSE);
_action_set_sensitive (browser, "AddNewsFeed", FALSE);
}
midori_location_action_set_security_hint (
MIDORI_LOCATION_ACTION (action), midori_view_get_security (view));
@ -484,19 +473,16 @@ midori_view_notify_icon_cb (MidoriView* view,
GParamSpec* pspec,
MidoriBrowser* browser)
{
const gchar* uri;
GtkAction* action;
if (midori_browser_get_current_tab (browser) != (GtkWidget*)view)
return;
uri = midori_view_get_display_uri (view);
action = _action_by_name (browser, "Location");
if (browser->maximum_history_age)
midori_location_action_set_icon_for_uri (
MIDORI_LOCATION_ACTION (action), midori_view_get_icon (view), uri);
midori_location_action_set_icon (MIDORI_LOCATION_ACTION (action),
midori_view_get_icon (view));
if (sokoke_is_app_or_private ())
gtk_window_set_icon (GTK_WINDOW (browser), midori_view_get_icon (view));
}
static void
@ -532,7 +518,7 @@ midori_view_notify_load_status_cb (GtkWidget* widget,
/* This is a hack to ensure that the address entry is focussed
with speed dial open. */
if (midori_view_is_blank (view))
gtk_action_activate (_action_by_name (browser, "Location"));
midori_browser_activate_action (browser, "Location");
}
g_object_notify (G_OBJECT (browser), "load-status");
@ -995,14 +981,9 @@ midori_browser_prepare_download (MidoriBrowser* browser,
return TRUE;
}
static gchar*
midori_browser_save_source (const gchar* uri,
const gchar* data,
const size_t len,
const gchar* outfile);
static void
midori_browser_save_uri (MidoriBrowser* browser,
MidoriView* view,
const gchar* uri)
{
static gchar* last_dir = NULL;
@ -1011,7 +992,6 @@ midori_browser_save_uri (MidoriBrowser* browser,
gchar* filename;
gchar* dirname;
gchar* last_slash;
gchar* folder;
if (!gtk_widget_get_visible (GTK_WIDGET (browser)))
return;
@ -1049,24 +1029,11 @@ midori_browser_save_uri (MidoriBrowser* browser,
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
{
GtkWidget* view;
GtkWidget* web_view;
WebKitWebDataSource *data_source;
WebKitWebFrame *frame;
const GString *data;
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog));
view = midori_browser_get_current_tab (browser);
web_view = midori_view_get_web_view (MIDORI_VIEW (view));
frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
data_source = webkit_web_frame_get_data_source (frame);
data = webkit_web_data_source_get_data (data_source);
if (data)
midori_browser_save_source (uri, data->str, data->len, filename);
midori_view_save_source (view, uri, filename);
g_free (last_dir);
last_dir = folder;
katze_assign (last_dir,
gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dialog)));
}
gtk_widget_destroy (dialog);
}
@ -1076,10 +1043,8 @@ midori_view_save_as_cb (GtkWidget* menuitem,
const gchar* uri,
GtkWidget* view)
{
MidoriBrowser* browser;
browser = midori_browser_get_for_widget (menuitem);
midori_browser_save_uri (browser, uri);
MidoriBrowser* browser = midori_browser_get_for_widget (view);
midori_browser_save_uri (browser, MIDORI_VIEW (view), uri);
}
static gchar*
@ -1185,7 +1150,7 @@ midori_view_activate_action_cb (GtkWidget* view,
const gchar* action,
MidoriBrowser* browser)
{
_midori_browser_activate_action (browser, action);
midori_browser_activate_action (browser, action);
}
static void
@ -1246,11 +1211,11 @@ midori_browser_view_copy_history (GtkWidget* view_to,
}
}
static gint
static gboolean
midori_browser_notify_new_tab_timeout_cb (MidoriBrowser *browser)
{
gtk_window_set_opacity (GTK_WINDOW (browser), 1);
return 0;
return G_SOURCE_REMOVE;
}
static void
@ -1351,20 +1316,129 @@ midori_view_download_save_as_response_cb (GtkWidget* dialog,
gtk_widget_hide (dialog);
}
static void
midori_browser_download_status_cb (WebKitDownload* download,
GParamSpec* pspec,
gpointer user_data)
{
const gchar* uri = webkit_download_get_destination_uri (download);
switch (webkit_download_get_status (download))
{
case WEBKIT_DOWNLOAD_STATUS_FINISHED:
if (!g_app_info_launch_default_for_uri (uri, NULL, NULL))
{
sokoke_message_dialog (GTK_MESSAGE_ERROR,
_("Error opening the image!"),
_("Can not open selected image in a default viewer."), FALSE);
}
break;
case WEBKIT_DOWNLOAD_STATUS_ERROR:
webkit_download_cancel (download);
sokoke_message_dialog (GTK_MESSAGE_ERROR,
_("Error downloading the image!"),
_("Can not downlaod selected image."), FALSE);
break;
case WEBKIT_DOWNLOAD_STATUS_CREATED:
case WEBKIT_DOWNLOAD_STATUS_STARTED:
case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
break;
}
}
static gchar*
midori_browser_download_prepare_filename (gchar* filename)
{
if (g_file_test (filename, G_FILE_TEST_EXISTS))
{
int i = 1;
const gchar* dot_pos;
const gchar* last_separator;
gchar* serial;
GString* tmp_filename;
gssize position;
last_separator = strrchr (filename, G_DIR_SEPARATOR);
dot_pos = strrchr ((last_separator) ? last_separator : filename, '.');
position = dot_pos ? (dot_pos - filename) : (gssize) strlen (filename);
tmp_filename = g_string_new (NULL);
do
{
serial = g_strdup_printf ("-%d", i++);
g_string_assign (tmp_filename, filename);
g_string_insert (tmp_filename, position, serial);
g_free (serial);
} while (g_file_test (tmp_filename->str, G_FILE_TEST_EXISTS));
g_free (filename);
filename = g_string_free (tmp_filename, FALSE);
}
return filename;
}
static gchar*
midori_browser_download_prepare_destination_uri (WebKitDownload* download,
const gchar* folder)
{
const gchar* suggested_filename;
GFile* file_source;
gchar* file_basename;
gchar* download_dir = NULL;
gchar* destination_uri;
gchar* destination_filename;
gchar* midori_tmp_dir;
suggested_filename = webkit_download_get_suggested_filename (download);
file_source = g_file_new_for_uri (suggested_filename);
file_basename = g_file_get_basename (file_source);
if (folder == NULL)
{
midori_tmp_dir = g_strconcat ("midori-", g_get_user_name (), NULL);
download_dir = g_build_filename (g_get_tmp_dir (), midori_tmp_dir, NULL);
g_free (midori_tmp_dir);
}
else
download_dir = (gchar*)folder;
destination_filename = g_build_filename (download_dir, file_basename, NULL);
destination_filename = midori_browser_download_prepare_filename (destination_filename);
destination_uri = g_filename_to_uri (destination_filename, NULL, NULL);
if (!g_file_test (download_dir, G_FILE_TEST_EXISTS))
katze_mkdir_with_parents (download_dir, 0700);
g_free (file_basename);
if (folder == NULL)
g_free (download_dir);
g_free (destination_filename);
g_object_unref (file_source);
return destination_uri;
}
static gboolean
midori_view_download_requested_cb (GtkWidget* view,
WebKitDownload* download,
MidoriBrowser* browser)
{
if (!webkit_download_get_destination_uri (download))
if (g_object_get_data (G_OBJECT (download), "open-in-viewer"))
{
gchar* destination_uri =
midori_browser_download_prepare_destination_uri (download, NULL);
midori_browser_prepare_download (browser, download, destination_uri);
g_signal_connect (download, "notify::status",
G_CALLBACK (midori_browser_download_status_cb), (gpointer) browser);
webkit_download_start (download);
g_free (destination_uri);
}
else if (!webkit_download_get_destination_uri (download))
{
gchar* folder;
if (g_object_get_data (G_OBJECT (download), "save-as-download"))
{
static GtkWidget* dialog = NULL;
if (!dialog)
{
gchar* folder;
dialog = sokoke_file_chooser_dialog_new (_("Save file"),
GTK_WINDOW (browser), GTK_FILE_CHOOSER_ACTION_SAVE);
gtk_file_chooser_set_do_overwrite_confirmation (
@ -1385,58 +1459,12 @@ midori_view_download_requested_cb (GtkWidget* view,
}
else
{
const gchar* suggested;
gchar* basename;
gchar* filename;
gchar* uri;
if (g_object_get_data (G_OBJECT (download), "open-download"))
folder = g_strdup (g_get_tmp_dir ());
else
folder = katze_object_get_string (browser->settings, "download-folder");
suggested = webkit_download_get_suggested_filename (download);
/* The suggested name may contain a folder name */
basename = g_path_get_basename (suggested);
filename = g_build_filename (folder, basename, NULL);
g_free (basename);
/* If the filename exists, choose a different name */
if (g_access (filename, F_OK) == 0)
{
/* Put the number in front of the extension */
gchar* extension = strrchr (filename, '.');
gsize length = extension ? (gsize)(extension - filename) : strlen (filename);
do
{
if (g_ascii_isdigit (filename[length - 1]))
filename[length - 1] += 1; /* FIXME: This will increment '9' to ':' */
else
{
gchar* new_filename;
if (extension)
{
/* Change the '.' to a '\0' to put the 0 in between */
*extension++ = '\0';
new_filename= g_strconcat (filename, "0.", extension, NULL);
}
else
new_filename = g_strconcat (filename, "0", NULL);
katze_assign (filename, new_filename);
if (extension)
{
extension = strrchr (filename, '.');
length = extension - filename;
}
else
length = strlen (filename);
}
}
while (g_access (filename, F_OK) == 0);
}
g_free (folder);
uri = g_filename_to_uri (filename, NULL, NULL);
g_free (filename);
midori_browser_prepare_download (browser, download, uri);
g_free (uri);
gchar* folder = g_object_get_data (G_OBJECT (download), "open-download")
? NULL : katze_object_get_string (browser->settings, "download-folder");
gchar* destination_uri =
midori_browser_download_prepare_destination_uri (download, folder);
midori_browser_prepare_download (browser, download, destination_uri);
g_free (destination_uri);
}
}
return TRUE;
@ -1488,20 +1516,18 @@ midori_browser_notebook_resize (MidoriBrowser* browser,
GdkRectangle* allocation)
{
gint new_size = 0;
gint n = gtk_notebook_get_n_pages (GTK_NOTEBOOK(browser->notebook));
gint n = MAX (1, gtk_notebook_get_n_pages (GTK_NOTEBOOK (browser->notebook)));
const gint max_size = 150;
gint min_size;
gint icon_size = 16;
GtkAllocation notebook_size;
GList* children;
g_return_if_fail (n > 0);
if (allocation != NULL)
notebook_size.width = allocation->width;
else
gtk_widget_get_allocation (browser->notebook, &notebook_size);
new_size = notebook_size.width / n - 7;
new_size = notebook_size.width / n;
gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (browser->notebook),
GTK_ICON_SIZE_MENU, &icon_size, NULL);
@ -1672,14 +1698,14 @@ midori_browser_key_press_event (GtkWidget* widget,
&& event->keyval == GDK_KEY_Tab
&& (event->state & GDK_CONTROL_MASK))
{
gtk_action_activate (_action_by_name (browser, "TabNext"));
midori_browser_activate_action (browser, "TabNext");
return TRUE;
}
else if (event->keyval == GDK_KEY_ISO_Left_Tab
&& (event->state & GDK_CONTROL_MASK)
&& (event->state & GDK_SHIFT_MASK))
{
gtk_action_activate (_action_by_name (browser, "TabPrevious"));
midori_browser_activate_action (browser, "TabPrevious");
return TRUE;
}
/* Interpret Ctrl+= as Zoom In for compatibility */
@ -1692,7 +1718,7 @@ midori_browser_key_press_event (GtkWidget* widget,
/* Interpret F5 as reloading for compatibility */
else if (event->keyval == GDK_KEY_F5)
{
gtk_action_activate (_action_by_name (browser, "Reload"));
midori_browser_activate_action (browser, "Reload");
return TRUE;
}
@ -1741,12 +1767,12 @@ midori_browser_key_press_event (GtkWidget* widget,
if ((event->keyval == GDK_KEY_BackSpace)
&& (event->state & GDK_SHIFT_MASK))
{
gtk_action_activate (_action_by_name (browser, "Forward"));
midori_browser_activate_action (browser, "Forward");
return TRUE;
}
else if (event->keyval == GDK_KEY_BackSpace)
{
gtk_action_activate (_action_by_name (browser, "Back"));
midori_browser_activate_action (browser, "Back");
return TRUE;
}
@ -2246,8 +2272,7 @@ static void
_action_private_browsing_activate (GtkAction* action,
MidoriBrowser* browser)
{
const gchar* uri = midori_browser_get_current_uri (browser);
sokoke_spawn_app (uri && *uri ? uri : "about:blank", TRUE);
sokoke_spawn_app ("about:private", TRUE);
}
static void
@ -2313,7 +2338,8 @@ static void
_action_save_as_activate (GtkAction* action,
MidoriBrowser* browser)
{
midori_browser_save_uri (browser, midori_browser_get_current_uri (browser));
GtkWidget* view = midori_browser_get_current_tab (browser);
midori_browser_save_uri (browser, MIDORI_VIEW (view), NULL);
}
static void
@ -2597,6 +2623,7 @@ _action_copy_activate (GtkAction* action,
MidoriBrowser* browser)
{
GtkWidget* widget = gtk_window_get_focus (GTK_WINDOW (browser));
#if !WEBKIT_CHECK_VERSION (1, 4, 3)
/* Work around broken clipboard handling for the sake of the user */
if (WEBKIT_IS_WEB_VIEW (widget))
{
@ -2606,6 +2633,7 @@ _action_copy_activate (GtkAction* action,
sokoke_widget_copy_clipboard (widget, selected);
return;
}
#endif
if (G_LIKELY (widget) && g_signal_lookup ("copy-clipboard", G_OBJECT_TYPE (widget)))
g_signal_emit_by_name (widget, "copy-clipboard");
}
@ -3032,6 +3060,7 @@ _action_compact_menu_populate_popup (GtkAction* action,
{ NULL },
{ "Fullscreen" },
{ "Panel" },
{ "Bookmarkbar" },
{ "-" },
#endif
{ NULL },
@ -3103,7 +3132,7 @@ midori_preferences_response_help_cb (GtkWidget* preferences,
MidoriBrowser* browser)
{
if (response == GTK_RESPONSE_HELP)
gtk_action_activate (_action_by_name (browser, "HelpFAQ"));
midori_browser_activate_action (browser, "HelpFAQ");
}
static void
@ -3129,6 +3158,19 @@ _action_preferences_activate (GtkAction* action,
gtk_window_present (GTK_WINDOW (dialog));
}
static gboolean
midori_browser_has_native_menubar (void)
{
#if HAVE_HILDON
return TRUE;
#else
static const gchar* ubuntu_menuproxy = NULL;
if (ubuntu_menuproxy == NULL)
ubuntu_menuproxy = g_getenv ("UBUNTU_MENUPROXY");
return ubuntu_menuproxy && strstr (ubuntu_menuproxy, ".so") != NULL;
#endif
}
static void
_action_menubar_activate (GtkToggleAction* menubar_action,
MidoriBrowser* browser)
@ -3139,6 +3181,9 @@ _action_menubar_activate (GtkToggleAction* menubar_action,
GList* children;
gchar* items;
if (midori_browser_has_native_menubar ())
active = FALSE;
toolbar_items = g_string_new (NULL);
children = gtk_container_get_children (GTK_CONTAINER (browser->navigationbar));
for (; children != NULL; children = g_list_next (children))
@ -3232,13 +3277,14 @@ _action_reload_stop_activate (GtkAction* action,
{
GdkModifierType state = (GdkModifierType)0;
gint x, y;
GdkWindow* window;
gboolean from_cache = TRUE;
if (!strcmp (gtk_action_get_name (action), "ReloadUncached"))
from_cache = FALSE;
else
else if ((window = gtk_widget_get_window (GTK_WIDGET (browser))))
{
gdk_window_get_pointer (NULL, &x, &y, &state);
gdk_window_get_pointer (window, &x, &y, &state);
if (state & GDK_SHIFT_MASK)
from_cache = FALSE;
}
@ -3304,112 +3350,19 @@ _action_view_encoding_activate (GtkAction* action,
}
}
static gchar*
midori_browser_get_uri_extension (const gchar* uri)
{
gchar* slash;
gchar* period;
gchar* ext_end;
/* Find the last slash in the URI and search for the last period
*after* the last slash. This is not completely accurate
but should cover most (simple) URIs */
slash = strrchr (uri, '/');
/* Huh, URI without slashes? */
if (!slash)
return NULL;
ext_end = period = strrchr (slash, '.');
if (!period)
return NULL;
/* Skip the period */
ext_end++;
/* If *ext_end is 0 here, the URI ended with a period, so skip */
if (!*ext_end)
return NULL;
/* Find the end of the extension */
while (*ext_end && g_ascii_isalnum (*ext_end))
ext_end++;
*ext_end = 0;
return g_strdup (period);
}
static gchar*
midori_browser_save_source (const gchar* uri,
const gchar* data,
const size_t len,
const gchar* outfile)
{
gchar* unique_filename;
gint fd;
FILE* fp;
size_t ret;
if (!outfile)
{
gchar* filename;
gchar* extension;
extension = midori_browser_get_uri_extension (uri);
filename = g_strdup_printf ("%uXXXXXX%s",
g_str_hash (uri), extension && *extension ? extension : ".htm");
g_free (extension);
fd = g_file_open_tmp (filename, &unique_filename, NULL);
g_free (filename);
}
else
{
unique_filename = g_strdup (outfile);
fd = g_open (unique_filename, O_WRONLY|O_CREAT, 0644);
}
if (fd != -1)
{
if ((fp = fdopen (fd, "w")))
{
ret = fwrite (data, 1, len, fp);
fclose (fp);
if ((ret - len) != 0)
{
g_warning ("Error writing to file %s "
"in midori_browser_source_transfer_cb()", unique_filename);
katze_assign (unique_filename, NULL);
}
}
close (fd);
}
return unique_filename;
}
static void
_action_source_view_activate (GtkAction* action,
MidoriBrowser* browser)
{
WebKitWebDataSource *data_source;
WebKitWebFrame *frame;
const GString *data;
GtkWidget* view;
GtkWidget* web_view;
gchar* text_editor;
gchar* filename = NULL;
const gchar* uri;
if (!(view = midori_browser_get_current_tab (browser)))
return;
filename = midori_view_save_source (MIDORI_VIEW (view), NULL, NULL);
g_object_get (browser->settings, "text-editor", &text_editor, NULL);
uri = midori_view_get_display_uri (MIDORI_VIEW (view));
web_view = midori_view_get_web_view (MIDORI_VIEW (view));
frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (web_view));
data_source = webkit_web_frame_get_data_source (frame);
data = webkit_web_data_source_get_data (data_source);
if (!data)
return;
filename = midori_browser_save_source (uri, data->str, data->len, NULL);
if (!(text_editor && *text_editor))
{
GtkWidget* source;
@ -3454,6 +3407,9 @@ _action_fullscreen_activate (GtkAction* action,
state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (browser)));
if (state & GDK_WINDOW_STATE_FULLSCREEN)
{
if (katze_object_get_boolean (G_OBJECT (browser->settings), "show-menubar"))
gtk_widget_show (browser->menubar);
if (katze_object_get_boolean (G_OBJECT (browser->settings), "show-panel"))
gtk_widget_show (browser->panel);
@ -3471,6 +3427,7 @@ _action_fullscreen_activate (GtkAction* action,
}
else
{
gtk_widget_hide (browser->menubar);
gtk_widget_hide (browser->panel);
gtk_widget_hide (browser->bookmarkbar);
gtk_widget_hide (browser->navigationbar);
@ -3680,51 +3637,49 @@ static void
_action_location_reset_uri (GtkAction* action,
MidoriBrowser* browser)
{
const gchar* uri;
uri = midori_browser_get_current_uri (browser);
midori_location_action_set_text (MIDORI_LOCATION_ACTION (action), uri);
GtkWidget* view;
if ((view = midori_browser_get_current_tab (browser)))
{
midori_location_action_set_text (MIDORI_LOCATION_ACTION (action),
midori_view_get_display_uri (MIDORI_VIEW (view)));
midori_location_action_set_icon (MIDORI_LOCATION_ACTION (action),
midori_view_get_icon (MIDORI_VIEW (view)));
}
}
static void
_action_location_submit_uri (GtkAction* action,
const gchar* uri,
gboolean new_tab,
MidoriBrowser* browser)
{
gchar* stripped_uri;
gchar* new_uri;
gint n;
stripped_uri = g_strdup (uri);
g_strstrip (stripped_uri);
new_uri = sokoke_magic_uri (stripped_uri);
uri = katze_skip_whitespace (uri);
new_uri = sokoke_magic_uri (uri);
if (!new_uri)
{
gchar** parts;
gchar* keywords = NULL;
const gchar* keywords = NULL;
const gchar* search_uri = NULL;
KatzeItem* item;
/* Do we have a keyword and a string? */
parts = g_strsplit (stripped_uri, " ", 2);
if (parts[0] && browser->search_engines)
if (browser->search_engines
&& (item = katze_array_find_token (browser->search_engines, uri)))
{
KatzeItem* item;
if ((item = katze_array_find_token (browser->search_engines, parts[0])))
{
keywords = g_strdup (parts[1] ? parts[1] : "");
keywords = strchr (uri, ' ');
if (keywords != NULL)
keywords++;
else
keywords = "";
search_uri = katze_item_get_uri (item);
}
}
g_strfreev (parts);
if (keywords)
g_free (stripped_uri);
else
if (keywords == NULL)
{
keywords = stripped_uri;
keywords = uri;
search_uri = browser->location_entry_search;
}
new_uri = midori_uri_for_search (search_uri, keywords);
@ -3753,11 +3708,7 @@ _action_location_submit_uri (GtkAction* action,
if (sqlite3_step (statement) == SQLITE_DONE)
sqlite3_clear_bindings (statement);
}
g_free (keywords);
}
else
g_free (stripped_uri);
if (new_tab)
{
@ -4348,7 +4299,6 @@ _action_bookmarks_export_activate (GtkAction* action,
const gchar* format;
gchar* path = NULL;
GError* error;
sqlite3* db;
KatzeArray* bookmarks;
if (!browser->bookmarks || !gtk_widget_get_visible (GTK_WIDGET (browser)))
@ -4389,9 +4339,8 @@ wrong_format:
return;
error = NULL;
db = g_object_get_data (G_OBJECT (browser->history), "db");
bookmarks = katze_array_new (KATZE_TYPE_ARRAY);
midori_bookmarks_export_array_db (db, bookmarks, "");
bookmarks = midori_array_query_recursive (browser->bookmarks,
"*", "folder='%q'", "", TRUE);
if (!midori_array_to_file (bookmarks, path, format, &error))
{
sokoke_message_dialog (GTK_MESSAGE_ERROR,
@ -4873,7 +4822,7 @@ midori_panel_cycle_child_focus_cb (GtkWidget* hpaned,
|| !gtk_widget_get_ancestor (focus, GTK_TYPE_PANED))
{
g_signal_stop_emission_by_name (hpaned, "cycle-child-focus");
gtk_action_activate (_action_by_name (browser, "Location"));
midori_browser_activate_action (browser, "Location");
return TRUE;
}
return FALSE;
@ -4947,7 +4896,7 @@ midori_panel_close_cb (MidoriPanel* panel,
}
static void
gtk_notebook_switch_page_cb (GtkWidget* notebook,
midori_browser_notebook_switch_page_cb (GtkWidget* notebook,
gpointer page,
guint page_num,
MidoriBrowser* browser)
@ -4966,7 +4915,7 @@ gtk_notebook_switch_page_cb (GtkWidget* notebook,
}
static void
gtk_notebook_switch_page_after_cb (GtkWidget* notebook,
midori_browser_notebook_switch_page_after_cb (GtkWidget* notebook,
gpointer page,
guint page_num,
MidoriBrowser* browser)
@ -4988,6 +4937,8 @@ gtk_notebook_switch_page_after_cb (GtkWidget* notebook,
midori_location_action_set_text (MIDORI_LOCATION_ACTION (action), uri);
midori_location_action_set_icon (MIDORI_LOCATION_ACTION (action),
midori_view_get_icon (view));
if (sokoke_is_app_or_private ())
gtk_window_set_icon (GTK_WINDOW (browser), midori_view_get_icon (view));
if (browser->proxy_array)
katze_item_set_meta_integer (KATZE_ITEM (browser->proxy_array), "current",
@ -5031,9 +4982,8 @@ midori_browser_notebook_create_window_cb (GtkNotebook* notebook,
g_signal_emit (browser, signals[NEW_WINDOW], 0, NULL, &new_browser);
if (new_browser)
{
GtkWidget* new_notebook = katze_object_get_object (new_browser, "notebook");
g_object_unref (new_notebook);
gtk_window_move (GTK_WINDOW (browser), x, y);
GtkWidget* new_notebook = new_browser->notebook;
gtk_window_move (GTK_WINDOW (new_browser), x, y);
return new_notebook;
}
else /* No MidoriApp, so this is app or private mode */
@ -5041,7 +4991,7 @@ midori_browser_notebook_create_window_cb (GtkNotebook* notebook,
}
static void
midori_browser_switch_tab_cb (GtkWidget* menuitem,
midori_browser_menu_item_switch_tab_cb (GtkWidget* menuitem,
MidoriBrowser* browser)
{
gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menuitem), "index"));
@ -5091,7 +5041,7 @@ midori_browser_notebook_button_press_event_after_cb (GtkNotebook* notebook,
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_object_set_data (G_OBJECT (menuitem), "index", GINT_TO_POINTER (i));
g_signal_connect (menuitem, "activate",
G_CALLBACK (midori_browser_switch_tab_cb), browser);
G_CALLBACK (midori_browser_menu_item_switch_tab_cb), browser);
i++;
}
g_list_free (tabs);
@ -5416,19 +5366,6 @@ midori_browser_window_state_event_cb (MidoriBrowser* browser,
else if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
window_state = MIDORI_WINDOW_FULLSCREEN;
g_object_set (browser->settings, "last-window-state", window_state, NULL);
if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
{
if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
{
gtk_widget_hide (browser->menubar);
}
else
{
if (katze_object_get_boolean (browser->settings, "show-menubar"))
gtk_widget_show (browser->menubar);
}
}
}
static gboolean
@ -5857,8 +5794,8 @@ midori_browser_init (MidoriBrowser* browser)
GtkSettings* gtk_settings;
GtkWidget* hpaned;
GtkWidget* vpaned;
GtkRcStyle* rcstyle;
GtkWidget* scrolled;
KatzeArray* dummy_array;
browser->settings = midori_web_settings_new ();
browser->proxy_array = katze_array_new (KATZE_TYPE_ARRAY);
@ -5878,6 +5815,9 @@ midori_browser_init (MidoriBrowser* browser)
G_CALLBACK (midori_browser_destroy_cb), NULL);
gtk_window_set_role (GTK_WINDOW (browser), "browser");
gtk_window_set_icon_name (GTK_WINDOW (browser), "web-browser");
#if GTK_CHECK_VERSION (3, 4, 0)
gtk_window_set_hide_titlebar_when_maximized (GTK_WINDOW (browser), TRUE);
#endif
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (browser), vbox);
gtk_widget_show (vbox);
@ -5911,7 +5851,7 @@ midori_browser_init (MidoriBrowser* browser)
}
/* Hide the 'Dummy' which only holds otherwise unused actions */
g_object_set (_action_by_name (browser, "Dummy"), "visible", FALSE, NULL);
_action_set_visible (browser, "Dummy", FALSE);
action = g_object_new (KATZE_TYPE_SEPARATOR_ACTION,
"name", "Separator",
@ -5986,12 +5926,14 @@ midori_browser_init (MidoriBrowser* browser)
gtk_action_group_add_action_with_accel (browser->action_group, action, "");
g_object_unref (action);
dummy_array = katze_array_new (KATZE_TYPE_ARRAY);
katze_array_update (dummy_array);
action = g_object_new (KATZE_TYPE_ARRAY_ACTION,
"name", "Bookmarks",
"label", _("_Bookmarks"),
"stock-id", STOCK_BOOKMARKS,
"tooltip", _("Show the saved bookmarks"),
"array", browser->proxy_array, /* Use a non-empty array here */
"array", dummy_array /* updated, unique */,
NULL);
g_object_connect (action,
"signal::populate-folder",
@ -6001,12 +5943,15 @@ midori_browser_init (MidoriBrowser* browser)
NULL);
gtk_action_group_add_action_with_accel (browser->action_group, action, "");
g_object_unref (action);
g_object_unref (dummy_array);
dummy_array = katze_array_new (KATZE_TYPE_ITEM);
katze_array_update (dummy_array);
action = g_object_new (KATZE_TYPE_ARRAY_ACTION,
"name", "Tools",
"label", _("_Tools"),
"stock-id", GTK_STOCK_PREFERENCES,
"array", katze_array_new (KATZE_TYPE_ITEM),
"array", dummy_array /* updated, unique */,
NULL);
g_object_connect (action,
"signal::populate-popup",
@ -6016,6 +5961,7 @@ midori_browser_init (MidoriBrowser* browser)
NULL);
gtk_action_group_add_action (browser->action_group, action);
g_object_unref (action);
g_object_unref (dummy_array);
action = g_object_new (KATZE_TYPE_ARRAY_ACTION,
"name", "Window",
@ -6051,6 +5997,7 @@ midori_browser_init (MidoriBrowser* browser)
browser->menubar = gtk_ui_manager_get_widget (ui_manager, "/menubar");
gtk_box_pack_start (GTK_BOX (vbox), browser->menubar, FALSE, FALSE, 0);
gtk_widget_hide (browser->menubar);
_action_set_visible (browser, "Menubar", !midori_browser_has_native_menubar ());
#if HAVE_HILDON
#if HILDON_CHECK_VERSION (2, 2, 0)
browser->menubar = hildon_app_menu_new ();
@ -6082,7 +6029,6 @@ midori_browser_init (MidoriBrowser* browser)
#endif
gtk_menu_shell_append (GTK_MENU_SHELL (browser->menubar), menuitem);
#endif
browser->menu_tools = gtk_menu_new ();
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (
gtk_ui_manager_get_widget (ui_manager, "/menubar/File/WindowNew")), NULL);
@ -6105,9 +6051,6 @@ midori_browser_init (MidoriBrowser* browser)
g_signal_connect (forward, "button-press-event",
G_CALLBACK (midori_browser_menu_item_middle_click_event_cb), browser);
#if HAVE_HILDON
_action_set_visible (browser, "Menubar", FALSE);
#endif
_action_set_sensitive (browser, "EncodingCustom", FALSE);
_action_set_visible (browser, "LastSession", FALSE);
#if !HAVE_HILDON && !defined (GDK_WINDOWING_X11)
@ -6207,18 +6150,22 @@ midori_browser_init (MidoriBrowser* browser)
gtk_paned_pack2 (GTK_PANED (hpaned), vpaned, TRUE, FALSE);
gtk_widget_show (vpaned);
browser->notebook = gtk_notebook_new ();
#if !GTK_CHECK_VERSION (3, 0, 0)
{
/* Remove the inner border between scrollbars and the window border */
rcstyle = gtk_rc_style_new ();
GtkRcStyle* rcstyle = gtk_rc_style_new ();
rcstyle->xthickness = 0;
gtk_widget_modify_style (browser->notebook, rcstyle);
g_object_unref (rcstyle);
}
#endif
gtk_notebook_set_scrollable (GTK_NOTEBOOK (browser->notebook), TRUE);
gtk_paned_pack1 (GTK_PANED (vpaned), browser->notebook, FALSE, FALSE);
g_signal_connect (browser->notebook, "switch-page",
G_CALLBACK (gtk_notebook_switch_page_cb),
G_CALLBACK (midori_browser_notebook_switch_page_cb),
browser);
g_signal_connect_after (browser->notebook, "switch-page",
G_CALLBACK (gtk_notebook_switch_page_after_cb),
G_CALLBACK (midori_browser_notebook_switch_page_after_cb),
browser);
g_signal_connect (browser->notebook, "page-reordered",
G_CALLBACK (midori_browser_notebook_page_reordered_cb),
@ -6285,7 +6232,12 @@ midori_browser_init (MidoriBrowser* browser)
gtk_box_pack_start (GTK_BOX (vbox), browser->statusbar, FALSE, FALSE, 0);
browser->transferbar = g_object_new (MIDORI_TYPE_TRANSFERBAR, NULL);
#if GTK_CHECK_VERSION (3, 0, 0)
/* FIXME: Transfers should go between text and statusbar features like GTK+2 */
gtk_box_pack_end (GTK_BOX (browser->statusbar_contents), browser->transferbar, FALSE, FALSE, 3);
#else
gtk_box_pack_start (GTK_BOX (browser->statusbar_contents), browser->transferbar, FALSE, FALSE, 3);
#endif
gtk_toolbar_set_show_arrow (GTK_TOOLBAR (browser->transferbar), FALSE);
gtk_widget_show (browser->transferbar);
@ -6676,29 +6628,21 @@ midori_browser_settings_notify (MidoriWebSettings* web_settings,
_toggle_tabbar_smartly (browser, FALSE);
else if (name == g_intern_string ("show-menubar"))
{
gtk_toggle_action_set_active (
GTK_TOGGLE_ACTION (_action_by_name (browser, "Menubar")),
g_value_get_boolean (&value));
_action_set_active (browser, "Menubar", g_value_get_boolean (&value));
}
else if (name == g_intern_string ("show-navigationbar"))
{
browser->show_navigationbar = g_value_get_boolean (&value);
gtk_toggle_action_set_active (
GTK_TOGGLE_ACTION (_action_by_name (browser, "Navigationbar")),
g_value_get_boolean (&value));
_action_set_active (browser, "Navigationbar", g_value_get_boolean (&value));
}
else if (name == g_intern_string ("show-bookmarkbar"))
{
gtk_toggle_action_set_active (
GTK_TOGGLE_ACTION (_action_by_name (browser, "Bookmarkbar")),
g_value_get_boolean (&value));
_action_set_active (browser, "Bookmarkbar", g_value_get_boolean (&value));
}
else if (name == g_intern_string ("show-statusbar"))
{
browser->show_statusbar = g_value_get_boolean (&value);
gtk_toggle_action_set_active (
GTK_TOGGLE_ACTION (_action_by_name (browser, "Statusbar")),
g_value_get_boolean (&value));
_action_set_active (browser, "Statusbar", g_value_get_boolean (&value));
}
else if (name == g_intern_string ("location-entry-search"))
{
@ -7336,7 +7280,7 @@ midori_browser_set_current_page (MidoriBrowser* browser,
gtk_notebook_set_current_page (GTK_NOTEBOOK (browser->notebook), n);
if (midori_view_is_blank (MIDORI_VIEW (view)))
gtk_action_activate (_action_by_name (browser, "Location"));
midori_browser_activate_action (browser, "Location");
else
gtk_widget_grab_focus (view);

View file

@ -277,43 +277,96 @@ midori_location_action_create_model (void)
}
static void
midori_location_action_popup_position (GtkWidget* popup,
GtkWidget* widget)
midori_location_action_popup_position (MidoriLocationAction* action,
gint matches)
{
GtkWidget* popup = action->popup;
GtkWidget* widget = action->entry;
GdkWindow* window = gtk_widget_get_window (widget);
gint wx, wy;
gint wx, wy, x_border, y_border, items;
GtkRequisition menu_req;
GtkRequisition widget_req;
GdkScreen* screen;
gint monitor_num;
GdkRectangle monitor;
GtkAllocation allocation;
GtkAllocation alloc;
gint height, sep, width, toplevel_height;
gboolean above;
GtkWidget* scrolled = gtk_widget_get_parent (action->treeview);
GtkWidget* toplevel;
GtkTreePath* path;
if (!window)
return;
gtk_widget_get_allocation (widget, &alloc);
#if GTK_CHECK_VERSION (3, 0, 0)
gtk_widget_get_preferred_size (widget, &widget_req, NULL);
#else
gtk_widget_size_request (widget, &widget_req);
#endif
gdk_window_get_origin (window, &wx, &wy);
if (!gtk_widget_get_has_window (widget))
{
GtkAllocation alloc;
gtk_widget_get_allocation (widget, &alloc);
#if GTK_CHECK_VERSION (3, 0, 0)
wx += alloc.x;
wy += alloc.y;
}
wy += alloc.y + (alloc.height - widget_req.height) / 2;
#endif
/* _gtk_entry_get_borders (GTK_ENTRY (widget), &x_border, &y_border); */
x_border = y_border = 0;
gtk_widget_size_request (popup, &menu_req);
gtk_widget_size_request (widget, &widget_req);
gtk_tree_view_column_cell_get_size (
gtk_tree_view_get_column (GTK_TREE_VIEW (action->treeview), 0),
NULL, NULL, NULL, NULL, &height);
gtk_widget_style_get (action->treeview, "vertical-separator", &sep, NULL);
height += sep;
/* Constrain to screen/ window size */
screen = gtk_widget_get_screen (widget);
monitor_num = gdk_screen_get_monitor_at_window (screen, window);
gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
if (wy + widget_req.height + menu_req.height <= monitor.y + monitor.height
|| wy - monitor.y < (monitor.y + monitor.height) - (wy + widget_req.height))
wy += widget_req.height;
toplevel = gtk_widget_get_toplevel (widget);
gtk_window_get_size (GTK_WINDOW (toplevel), NULL, &toplevel_height);
toplevel_height = MIN (toplevel_height, monitor.height);
if (wy > toplevel_height / 2)
items = MIN (matches, ((monitor.y + wy) / height) - 1);
else
items = MIN (matches, ((toplevel_height - wy) / height) - 1);
width = MIN (alloc.width, monitor.width) - 2 * x_border;
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (action->treeview));
#if GTK_CHECK_VERSION (3, 0, 0)
gtk_widget_set_size_request (scrolled, width, -1);
gtk_scrolled_window_set_min_content_width (GTK_SCROLLED_WINDOW (scrolled), width);
gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (scrolled), items * height);
gtk_widget_get_preferred_size (popup, &menu_req, NULL);
#else
gtk_widget_set_size_request (scrolled, width, items * height);
gtk_widget_size_request (popup, &menu_req);
#endif
if (wx < monitor.x)
wx = monitor.x;
else if (wx + menu_req.width > monitor.x + monitor.width)
wx = monitor.x + monitor.width - menu_req.width;
if (wy + widget_req.height + menu_req.height <= monitor.y + monitor.height ||
wy - monitor.y < (monitor.y + monitor.height) - (wy + widget_req.height))
{
wy += widget_req.height;
above = FALSE;
}
else
{
wy -= menu_req.height;
above = TRUE;
}
path = gtk_tree_path_new_from_indices (above ? matches - 1 : 0, -1);
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (action->treeview), path,
NULL, FALSE, 0.0, 0.0);
gtk_tree_path_free (path);
gtk_window_move (GTK_WINDOW (popup), wx, wy);
gtk_widget_get_allocation (widget, &allocation);
gtk_window_resize (GTK_WINDOW (popup), allocation.width, 1);
}
static gboolean
@ -357,28 +410,21 @@ midori_location_action_popup_timeout_cb (gpointer data)
gint result;
static sqlite3_stmt* stmt;
const gchar* sqlcmd;
gint matches, searches, height, screen_height, browser_height, sep;
MidoriBrowser* browser;
gint matches, searches;
GtkStyle* style;
if (!action->entry || !gtk_widget_has_focus (action->entry) || !action->history)
return FALSE;
/* No completion when typing a search token */
if (action->search_engines != NULL)
if (action->search_engines
&& katze_array_find_token (action->search_engines, action->key))
{
gchar** parts = g_strsplit (action->key, " ", 2);
if (parts && *parts && parts[1]
&& katze_array_find_token (action->search_engines, *parts))
{
g_strfreev (parts);
midori_location_action_popdown_completion (action);
return FALSE;
}
g_strfreev (parts);
}
/* Empaty string or starting with a space means: no completion */
/* Empty string or starting with a space means: no completion */
if (!(action->key && *action->key && *action->key != ' '))
{
midori_location_action_popdown_completion (action);
@ -443,6 +489,8 @@ midori_location_action_popup_timeout_cb (gpointer data)
popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_type_hint (GTK_WINDOW (popup), GDK_WINDOW_TYPE_HINT_COMBO);
/* Window managers may ignore programmatic resize without this */
gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
popup_frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (popup_frame), GTK_SHADOW_ETCHED_IN);
gtk_container_add (GTK_CONTAINER (popup), popup_frame);
@ -489,6 +537,7 @@ midori_location_action_popup_timeout_cb (gpointer data)
gtk_list_store_clear (store);
matches = searches = 0;
gtk_widget_realize (action->treeview);
style = gtk_widget_get_style (action->treeview);
while (result == SQLITE_ROW)
{
@ -507,9 +556,11 @@ midori_location_action_popup_timeout_cb (gpointer data)
else if (type == 2 /* search_view */)
{
gchar* search_title = g_strdup_printf (_("Search for %s"), title);
gchar* search_desc = g_strdup_printf ("%s\n%s", search_title, uri);
gtk_list_store_insert_with_values (store, NULL, matches,
URI_COL, uri, TITLE_COL, search_title, YALIGN_COL, 0.25,
URI_COL, uri, TITLE_COL, search_desc, YALIGN_COL, 0.25,
STYLE_COL, 1, FAVICON_COL, icon, -1);
g_free (search_desc);
g_free (search_title);
}
if (icon != NULL)
@ -518,8 +569,12 @@ midori_location_action_popup_timeout_cb (gpointer data)
matches++;
result = sqlite3_step (stmt);
}
if (stmt)
{
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
}
if (action->search_engines)
{
@ -529,17 +584,22 @@ midori_location_action_popup_timeout_cb (gpointer data)
{
gchar* uri;
gchar* title;
const gchar* text;
gchar* desc;
GdkPixbuf* icon;
uri = midori_uri_for_search (katze_item_get_uri (item), action->key);
title = g_strdup_printf (_("Search with %s"), katze_item_get_name (item));
text = katze_item_get_text (item);
desc = g_strdup_printf ("%s\n%s", title, text ? text : uri);
icon = midori_search_action_get_icon (item, action->treeview, NULL, FALSE);
gtk_list_store_insert_with_values (store, NULL, matches + i,
URI_COL, uri, TITLE_COL, title, YALIGN_COL, 0.25,
URI_COL, uri, TITLE_COL, desc, YALIGN_COL, 0.25,
BACKGROUND_COL, style ? &style->bg[GTK_STATE_NORMAL] : NULL,
STYLE_COL, 1, FAVICON_COL, icon, -1);
g_free (uri);
g_free (title);
g_free (desc);
if (icon != NULL)
g_object_unref (icon);
i++;
@ -553,22 +613,10 @@ midori_location_action_popup_timeout_cb (gpointer data)
gtk_window_set_screen (GTK_WINDOW (action->popup),
gtk_widget_get_screen (action->entry));
gtk_window_set_transient_for (GTK_WINDOW (action->popup), GTK_WINDOW (toplevel));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW (action->treeview));
gtk_widget_show_all (action->popup);
}
browser = midori_browser_get_for_widget (action->entry);
column = gtk_tree_view_get_column (GTK_TREE_VIEW (action->treeview), 0);
gtk_tree_view_column_cell_get_size (column, NULL, NULL, NULL, NULL, &height);
screen_height = gdk_screen_get_height (gtk_widget_get_screen (action->popup));
gtk_window_get_size (GTK_WINDOW (browser), NULL, &browser_height);
screen_height = MIN (MIN (browser_height, screen_height / 1.5), screen_height / 1.5);
gtk_widget_style_get (action->treeview, "vertical-separator", &sep, NULL);
/* FIXME: Instead of 1.5 we should relate to the height of one line */
height = MIN (matches * height + (matches + searches) * sep
+ searches * height / 1.5, screen_height);
gtk_widget_set_size_request (action->treeview, -1, height);
midori_location_action_popup_position (action->popup, action->entry);
gtk_widget_show_all (action->popup);
midori_location_action_popup_position (action, matches + searches);
return FALSE;
}
@ -1532,30 +1580,22 @@ midori_location_action_add_item (MidoriLocationAction* location_action,
#endif
}
/**
* midori_location_action_set_icon_for_uri:
* @location_action: a #MidoriLocationAction
* @icon: a #GdkPixbuf
* @uri: an URI string
*
* Sets the icon for the specified URI.
*
* Deprecated: 0.4.4
**/
void
midori_location_action_set_icon_for_uri (MidoriLocationAction* location_action,
GdkPixbuf* icon,
const gchar* uri)
{
#if !HAVE_HILDON
GSList* proxies;
#endif
g_return_if_fail (MIDORI_IS_LOCATION_ACTION (location_action));
g_return_if_fail (!icon || GDK_IS_PIXBUF (icon));
g_return_if_fail (uri != NULL);
#if !HAVE_HILDON
proxies = gtk_action_get_proxies (GTK_ACTION (location_action));
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
GtkWidget* entry = midori_location_action_entry_for_proxy (proxies->data);
gtk_icon_entry_set_icon_from_pixbuf (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_PRIMARY, icon);
}
#endif
midori_location_action_set_icon (location_action, icon);
}
/**
@ -1659,16 +1699,20 @@ midori_location_action_set_security_hint (MidoriLocationAction* location_action,
for (; proxies != NULL; proxies = g_slist_next (proxies))
if (GTK_IS_TOOL_ITEM (proxies->data))
{
#if !GTK_CHECK_VERSION (3, 0, 0)
const gchar* bg_color = NULL;
const gchar* fg_color = NULL;
#endif
GtkWidget* entry = midori_location_action_entry_for_proxy (proxies->data);
GdkScreen* screen = gtk_widget_get_screen (entry);
GtkIconTheme* icon_theme = gtk_icon_theme_get_for_screen (screen);
if (hint == MIDORI_SECURITY_UNKNOWN)
{
#if !GTK_CHECK_VERSION (3, 0, 0)
bg_color = "#ef7070";
fg_color = "#000";
#endif
#if !HAVE_HILDON
if (gtk_icon_theme_has_icon (icon_theme, "channel-insecure-symbolic"))
gtk_icon_entry_set_icon_from_icon_name (GTK_ICON_ENTRY (entry),
@ -1685,8 +1729,10 @@ midori_location_action_set_security_hint (MidoriLocationAction* location_action,
}
else if (hint == MIDORI_SECURITY_TRUSTED)
{
#if !GTK_CHECK_VERSION (3, 0, 0)
bg_color = "#d1eeb9";
fg_color = "#000";
#endif
#if !HAVE_HILDON
if (gtk_icon_theme_has_icon (icon_theme, "channel-secure-symbolic"))
gtk_icon_entry_set_icon_from_icon_name (GTK_ICON_ENTRY (entry),

View file

@ -393,6 +393,13 @@ midori_preferences_set_settings (MidoriPreferences* preferences,
INDENTED_ADD (button);
button = katze_property_proxy (settings, "flash-window-on-new-bg-tabs", NULL);
SPANNED_ADD (button);
if (katze_object_has_property (settings, "enable-webgl"))
{
button = katze_property_proxy (settings, "enable-webgl", NULL);
INDENTED_ADD (button);
}
FRAME_NEW (NULL);
button = katze_property_label (settings, "preferred-languages");
INDENTED_ADD (button);

View file

@ -312,6 +312,7 @@ midori_search_action_create_tool_item (GtkAction* action)
toolitem = GTK_WIDGET (gtk_tool_item_new ());
entry = gtk_icon_entry_new ();
sokoke_entry_set_clear_button_visible (GTK_ENTRY (entry), TRUE);
gtk_icon_entry_set_icon_highlight (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_PRIMARY, TRUE);
alignment = gtk_alignment_new (0, 0.5, 1, 0.1);
@ -876,12 +877,6 @@ midori_search_action_editor_name_changed_cb (GtkWidget* entry,
GTK_RESPONSE_ACCEPT, text && *text);
}
static inline const gchar*
STR_NON_NULL (const gchar* string)
{
return string ? string : "";
}
static void
midori_search_action_get_editor (MidoriSearchAction* search_action,
gboolean new_engine)
@ -942,7 +937,7 @@ midori_search_action_get_editor (MidoriSearchAction* search_action,
gtk_entry_set_activates_default (GTK_ENTRY (entry_name), TRUE);
if (!new_engine)
gtk_entry_set_text (GTK_ENTRY (entry_name),
STR_NON_NULL (katze_item_get_name (item)));
katze_str_non_null (katze_item_get_name (item)));
gtk_box_pack_start (GTK_BOX (hbox), entry_name, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (content_area), hbox);
gtk_widget_show_all (hbox);
@ -956,7 +951,7 @@ midori_search_action_get_editor (MidoriSearchAction* search_action,
gtk_entry_set_activates_default (GTK_ENTRY (entry_description), TRUE);
if (!new_engine)
gtk_entry_set_text (GTK_ENTRY (entry_description)
, STR_NON_NULL (katze_item_get_text (item)));
, katze_str_non_null (katze_item_get_text (item)));
gtk_box_pack_start (GTK_BOX (hbox), entry_description, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (content_area), hbox);
gtk_widget_show_all (hbox);
@ -972,10 +967,11 @@ midori_search_action_get_editor (MidoriSearchAction* search_action,
#else
NULL);
#endif
g_object_set_data (G_OBJECT (entry_uri), "allow_%s", (void*)1);
gtk_entry_set_activates_default (GTK_ENTRY (entry_uri), TRUE);
if (!new_engine)
gtk_entry_set_text (GTK_ENTRY (entry_uri)
, STR_NON_NULL (katze_item_get_uri (item)));
, katze_str_non_null (katze_item_get_uri (item)));
gtk_box_pack_start (GTK_BOX (hbox), entry_uri, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (content_area), hbox);
gtk_widget_show_all (hbox);
@ -989,7 +985,7 @@ midori_search_action_get_editor (MidoriSearchAction* search_action,
gtk_entry_set_activates_default (GTK_ENTRY (entry_icon), TRUE);
if (!new_engine)
gtk_entry_set_text (GTK_ENTRY (entry_icon)
, STR_NON_NULL (katze_item_get_icon (item)));
, katze_str_non_null (katze_item_get_icon (item)));
gtk_box_pack_start (GTK_BOX (hbox), entry_icon, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (content_area), hbox);
gtk_widget_show_all (hbox);
@ -1003,7 +999,7 @@ midori_search_action_get_editor (MidoriSearchAction* search_action,
gtk_entry_set_activates_default (GTK_ENTRY (entry_token), TRUE);
if (!new_engine)
gtk_entry_set_text (GTK_ENTRY (entry_token)
, STR_NON_NULL (katze_item_get_token (item)));
, katze_str_non_null (katze_item_get_token (item)));
gtk_box_pack_start (GTK_BOX (hbox), entry_token, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (content_area), hbox);
gtk_widget_show_all (hbox);

File diff suppressed because it is too large Load diff

View file

@ -197,6 +197,11 @@ midori_view_can_view_source (MidoriView* view);
gboolean
midori_view_can_save (MidoriView* view);
gchar*
midori_view_save_source (MidoriView* view,
const gchar* uri,
const gchar* outfile);
gboolean
midori_view_can_find (MidoriView* view);

View file

@ -13,6 +13,7 @@
#include "midori-websettings.h"
#include "sokoke.h"
#include <midori/midori-core.h> /* Vala API */
#include <glib/gi18n.h>
#include <glib/gstdio.h>
@ -85,6 +86,7 @@ struct _MidoriWebSettings
gint clear_private_data;
gchar* clear_data;
gchar* site_data_rules;
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
gboolean enable_dns_prefetching;
#endif
@ -145,6 +147,8 @@ enum
PROP_OPEN_TABS_NEXT_TO_CURRENT,
PROP_OPEN_POPUPS_IN_TABS,
PROP_FLASH_WINDOW_ON_BG_TABS,
PROP_ENABLE_WEBGL,
PROP_ENABLE_FULLSCREEN,
PROP_AUTO_LOAD_IMAGES,
PROP_ENABLE_SCRIPTS,
@ -173,6 +177,7 @@ enum
PROP_CLEAR_PRIVATE_DATA,
PROP_CLEAR_DATA,
PROP_SITE_DATA_RULES,
PROP_ENABLE_DNS_PREFETCHING,
PROP_STRIP_REFERER,
PROP_ENFORCE_FONT_FAMILY,
@ -339,6 +344,21 @@ midori_get_download_dir (void)
}
return g_get_home_dir ();
}
static gboolean
midori_web_settings_low_memory_profile ()
{
gchar* contents;
const gchar* total;
if (!g_file_get_contents ("/proc/meminfo", &contents, NULL, NULL))
return FALSE;
if (contents && (total = strstr (contents, "MemTotal:")) && *total)
{
const gchar* value = katze_skip_whitespace (total + 9);
gdouble mem_total = g_ascii_strtoll (value, NULL, 0);
return mem_total / 1024.0 < 352 + 1;
}
return FALSE;
}
static void
midori_web_settings_class_init (MidoriWebSettingsClass* class)
@ -727,13 +747,8 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
"enable-plugins",
_("Enable Netscape plugins"),
_("Enable embedded Netscape plugin objects"),
#ifdef G_OS_WIN32
FALSE,
G_PARAM_READABLE));
#else
TRUE,
flags));
#endif
midori_web_settings_has_plugin_support (),
midori_web_settings_has_plugin_support () ? flags : G_PARAM_READABLE));
/* Override properties to override defaults */
g_object_class_install_property (gobject_class,
PROP_ENABLE_DEVELOPER_EXTRAS,
@ -777,7 +792,7 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
g_param_spec_boolean ("enable-page-cache",
"Enable page cache",
"Whether the page cache should be used",
TRUE,
!midori_web_settings_low_memory_profile (),
flags));
#endif
g_object_class_install_property (gobject_class,
@ -788,6 +803,26 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
_("Flash the browser window if a new tab was opened in the background"),
FALSE,
flags));
if (g_object_class_find_property (gobject_class, "enable-webgl"))
g_object_class_install_property (gobject_class,
PROP_ENABLE_WEBGL,
g_param_spec_boolean (
"enable-webgl",
_("Enable WebGL support"),
_("Allow websites to use OpenGL rendering"),
/* Enable by default for git builds */
!g_str_equal (PACKAGE_VERSION, MIDORI_VERSION),
flags));
if (g_object_class_find_property (gobject_class, "enable-fullscreen"))
g_object_class_install_property (gobject_class,
PROP_ENABLE_FULLSCREEN,
g_param_spec_boolean (
"enable-fullscreen",
"Enable Fullscreen",
"Allow experimental fullscreen API",
TRUE,
flags));
/**
* MidoriWebSettings:zoom-text-and-images:
@ -1021,6 +1056,22 @@ midori_web_settings_class_init (MidoriWebSettingsClass* class)
_("The data selected for deletion"),
NULL,
flags));
/**
* MidoriWebSettings:site-data-rules:
*
* Rules for accepting, denying and preserving cookies and other data.
* See midori_web_settings_get_site_data_policy() for details.
*
* Since: 0.4.4
*/
g_object_class_install_property (gobject_class,
PROP_SITE_DATA_RULES,
g_param_spec_string (
"site-data-rules",
"Rules for accepting, denying and preserving cookies and other data",
"Cookies, HTML5 databases, local storage and application cache blocking",
NULL,
flags));
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
/**
* MidoriWebSettings:enable-dns-prefetching:
@ -1180,6 +1231,74 @@ midori_web_settings_finalize (GObject* object)
G_OBJECT_CLASS (midori_web_settings_parent_class)->finalize (object);
}
/**
* midori_web_settings_has_plugin_support:
*
* Determines if Netscape plugins are supported.
*
* Returns: %TRUE if Netscape plugins can be used
*
* Since: 0.4.4
**/
gboolean
midori_web_settings_has_plugin_support (void)
{
#ifdef G_OS_WIN32
return FALSE;
#else
return g_getenv ("MIDORI_UNARMED") == NULL
&& g_strcmp0 (g_getenv ("MOZ_PLUGIN_PATH"), "/");
#endif
}
/**
* midori_web_settings_get_site_data_policy:
*
* Tests if @uri may store site data.
*
* Returns: a #MidoriSiteDataPolicy
*
* Since: 0.4.4
**/
MidoriSiteDataPolicy
midori_web_settings_get_site_data_policy (MidoriWebSettings* settings,
const gchar* uri)
{
MidoriSiteDataPolicy policy = MIDORI_SITE_DATA_UNDETERMINED;
gchar* hostname;
const gchar* match;
g_return_val_if_fail (MIDORI_IS_WEB_SETTINGS (settings), policy);
if (!(settings->site_data_rules && *settings->site_data_rules))
return policy;
/*
* Values prefixed with "-" are always blocked
* Values prefixed with "+" are always accepted
* Values prefixed with "!" are not cleared in Clear Private Data
* FIXME: "*" is a wildcard
* FIXME: indicate type of storage the rule applies to
* FIXME: support matching of the whole URI
**/
hostname = midori_uri_parse_hostname (uri, NULL);
match = strstr (settings->site_data_rules, hostname ? hostname : uri);
if (match != NULL && match != settings->site_data_rules)
{
const gchar* prefix = match - 1;
if (*prefix == '-')
policy = MIDORI_SITE_DATA_BLOCK;
else if (*prefix == '+')
policy = MIDORI_SITE_DATA_ACCEPT;
else if (*prefix == '!')
policy = MIDORI_SITE_DATA_PRESERVE;
else
g_warning ("%s: Matched with no prefix '%s'", G_STRFUNC, match);
}
g_free (hostname);
return policy;
}
#if (!HAVE_OSX && defined (G_OS_UNIX)) || defined (G_OS_WIN32)
static gchar*
get_sys_name (gchar** architecture)
@ -1544,6 +1663,9 @@ midori_web_settings_set_property (GObject* object,
case PROP_CLEAR_DATA:
katze_assign (web_settings->clear_data, g_value_dup_string (value));
break;
case PROP_SITE_DATA_RULES:
katze_assign (web_settings->site_data_rules, g_value_dup_string (value));
break;
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
case PROP_ENABLE_DNS_PREFETCHING:
web_settings->enable_dns_prefetching = g_value_get_boolean (value);
@ -1574,6 +1696,14 @@ midori_web_settings_set_property (GObject* object,
case PROP_FLASH_WINDOW_ON_BG_TABS:
web_settings->flash_window_on_bg_tabs = g_value_get_boolean (value);
break;
case PROP_ENABLE_WEBGL:
g_object_set (web_settings, "WebKitWebSettings::enable-webgl",
g_value_get_boolean (value), NULL);
break;
case PROP_ENABLE_FULLSCREEN:
g_object_set (web_settings, "WebKitWebSettings::enable-fullscreen",
g_value_get_boolean (value), NULL);
break;
case PROP_USER_STYLESHEET_URI:
{
gint old_len = web_settings->user_stylesheet_uri_cached
@ -1845,6 +1975,9 @@ midori_web_settings_get_property (GObject* object,
case PROP_CLEAR_DATA:
g_value_set_string (value, web_settings->clear_data);
break;
case PROP_SITE_DATA_RULES:
g_value_set_string (value, web_settings->site_data_rules);
break;
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
case PROP_ENABLE_DNS_PREFETCHING:
g_value_set_boolean (value, web_settings->enable_dns_prefetching);
@ -1859,6 +1992,14 @@ midori_web_settings_get_property (GObject* object,
case PROP_FLASH_WINDOW_ON_BG_TABS:
g_value_set_boolean (value, web_settings->flash_window_on_bg_tabs);
break;
case PROP_ENABLE_WEBGL:
g_value_set_boolean (value, katze_object_get_boolean (web_settings,
"WebKitWebSettings::enable-webgl"));
break;
case PROP_ENABLE_FULLSCREEN:
g_value_set_boolean (value, katze_object_get_boolean (web_settings,
"WebKitWebSettings::enable-fullscreen"));
break;
case PROP_USER_STYLESHEET_URI:
g_value_take_string (value, katze_object_get_string (web_settings,
"WebKitWebSettings::user-stylesheet-uri"));

View file

@ -173,6 +173,21 @@ const gchar*
midori_web_settings_get_system_name (gchar** architecture,
gchar** platform);
gboolean
midori_web_settings_has_plugin_support (void);
typedef enum
{
MIDORI_SITE_DATA_UNDETERMINED,
MIDORI_SITE_DATA_BLOCK,
MIDORI_SITE_DATA_ACCEPT,
MIDORI_SITE_DATA_PRESERVE,
} MidoriSiteDataPolicy;
MidoriSiteDataPolicy
midori_web_settings_get_site_data_policy (MidoriWebSettings* settings,
const gchar* uri);
G_END_DECLS
#endif /* __MIDORI_WEB_SETTINGS_H__ */

View file

@ -54,9 +54,6 @@
#endif
#include "socket.h"
#if USE_SSL
# include <openssl/ssl.h>
#endif
#ifdef G_ENABLE_DEBUG
# define debug_print g_debug
@ -562,10 +559,6 @@ gboolean sock_has_read_data(SockInfo *sock)
#ifdef G_OS_WIN32
gulong val;
#if USE_SSL
if (sock->ssl)
return TRUE;
#endif
if (ioctlsocket(sock->sock, FIONREAD, &val) < 0) {
g_warning("sock_has_read_data(): ioctlsocket() failed: %d\n",
WSAGetLastError());
@ -595,22 +588,6 @@ static gboolean sock_check(GSource *source)
fd_set fds;
GIOCondition condition = sock->condition;
#if USE_SSL
if (sock->ssl) {
if (condition & G_IO_IN) {
if (SSL_pending(sock->ssl) > 0)
return TRUE;
if (SSL_want_write(sock->ssl))
condition |= G_IO_OUT;
}
if (condition & G_IO_OUT) {
if (SSL_want_read(sock->ssl))
condition |= G_IO_IN;
}
}
#endif
FD_ZERO(&fds);
FD_SET(sock->sock, &fds);
@ -647,19 +624,6 @@ guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func,
sock->callback = func;
sock->condition = condition;
sock->data = data;
#if USE_SSL
if (sock->ssl) {
GSource *source;
source = g_source_new(&sock_watch_funcs, sizeof(SockSource));
((SockSource *)source)->sock = sock;
g_source_set_priority(source, G_PRIORITY_DEFAULT);
g_source_set_can_recurse(source, FALSE);
return g_source_attach(source, NULL);
}
#endif
return g_io_add_watch(sock->sock_ch, condition, sock_watch_cb, sock);
}
@ -1375,10 +1339,6 @@ gint sock_read(SockInfo *sock, gchar *buf, gint len)
{
g_return_val_if_fail(sock != NULL, -1);
#if USE_SSL
if (sock->ssl)
return ssl_read(sock->ssl, buf, len);
#endif
return fd_read(sock->sock, buf, len);
}
@ -1394,44 +1354,10 @@ gint fd_read(gint fd, gchar *buf, gint len)
#endif
}
#if USE_SSL
gint ssl_read(SSL *ssl, gchar *buf, gint len)
{
gint err, ret;
if (SSL_pending(ssl) == 0) {
if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0)
return -1;
}
ret = SSL_read(ssl, buf, len);
switch ((err = SSL_get_error(ssl, ret))) {
case SSL_ERROR_NONE:
return ret;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
errno = EAGAIN;
return -1;
case SSL_ERROR_ZERO_RETURN:
return 0;
default:
g_warning("SSL_read() returned error %d, ret = %d\n", err, ret);
if (ret == 0)
return 0;
return -1;
}
}
#endif
gint sock_write(SockInfo *sock, const gchar *buf, gint len)
{
g_return_val_if_fail(sock != NULL, -1);
#if USE_SSL
if (sock->ssl)
return ssl_write(sock->ssl, buf, len);
#endif
return fd_write(sock->sock, buf, len);
}
@ -1459,34 +1385,10 @@ gint fd_write(gint fd, const gchar *buf, gint len)
#endif
}
#if USE_SSL
gint ssl_write(SSL *ssl, const gchar *buf, gint len)
{
gint ret;
ret = SSL_write(ssl, buf, len);
switch (SSL_get_error(ssl, ret)) {
case SSL_ERROR_NONE:
return ret;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
errno = EAGAIN;
return -1;
default:
return -1;
}
}
#endif
gint sock_write_all(SockInfo *sock, const gchar *buf, gint len)
{
g_return_val_if_fail(sock != NULL, -1);
#if USE_SSL
if (sock->ssl)
return ssl_write_all(sock->ssl, buf, len);
#endif
return fd_write_all(sock->sock, buf, len);
}
@ -1506,24 +1408,6 @@ gint fd_write_all(gint fd, const gchar *buf, gint len)
return wrlen;
}
#if USE_SSL
gint ssl_write_all(SSL *ssl, const gchar *buf, gint len)
{
gint n, wrlen = 0;
while (len) {
n = ssl_write(ssl, buf, len);
if (n <= 0)
return -1;
len -= n;
wrlen += n;
buf += n;
}
return wrlen;
}
#endif
gint fd_recv(gint fd, gchar *buf, gint len, gint flags)
{
#ifdef G_OS_WIN32
@ -1570,38 +1454,10 @@ gint fd_gets(gint fd, gchar *buf, gint len)
return bp - buf;
}
#if USE_SSL
gint ssl_gets(SSL *ssl, gchar *buf, gint len)
{
gchar *newline, *bp = buf;
gint n;
if (--len < 1)
return -1;
do {
if ((n = ssl_peek(ssl, bp, len)) <= 0)
return -1;
if ((newline = memchr(bp, '\n', n)) != NULL)
n = newline - bp + 1;
if ((n = ssl_read(ssl, bp, n)) < 0)
return -1;
bp += n;
len -= n;
} while (!newline && len);
*bp = '\0';
return bp - buf;
}
#endif
gint sock_gets(SockInfo *sock, gchar *buf, gint len)
{
g_return_val_if_fail(sock != NULL, -1);
#if USE_SSL
if (sock->ssl)
return ssl_gets(sock->ssl, buf, len);
#endif
return fd_gets(sock->sock, buf, len);
}
@ -1630,42 +1486,11 @@ gint fd_getline(gint fd, gchar **line)
return (gint)size;
}
#if USE_SSL
gint ssl_getline(SSL *ssl, gchar **line)
{
gchar buf[BUFFSIZE];
gchar *str = NULL;
gint len;
gulong size = 0;
gulong cur_offset = 0;
while ((len = ssl_gets(ssl, buf, sizeof(buf))) > 0) {
size += len;
str = g_realloc(str, size + 1);
memcpy(str + cur_offset, buf, len + 1);
cur_offset += len;
if (buf[len - 1] == '\n')
break;
}
*line = str;
if (!str)
return -1;
else
return (gint)size;
}
#endif
gint sock_getline(SockInfo *sock, gchar **line)
{
g_return_val_if_fail(sock != NULL, -1);
g_return_val_if_fail(line != NULL, -1);
#if USE_SSL
if (sock->ssl)
return ssl_getline(sock->ssl, line);
#endif
return fd_getline(sock->sock, line);
}
@ -1679,44 +1504,10 @@ gint sock_puts(SockInfo *sock, const gchar *buf)
}
/* peek at the socket data without actually reading it */
#if USE_SSL
gint ssl_peek(SSL *ssl, gchar *buf, gint len)
{
gint err, ret;
if (SSL_pending(ssl) == 0) {
if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0)
return -1;
}
ret = SSL_peek(ssl, buf, len);
switch ((err = SSL_get_error(ssl, ret))) {
case SSL_ERROR_NONE:
return ret;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
errno = EAGAIN;
return -1;
case SSL_ERROR_ZERO_RETURN:
return 0;
default:
g_warning("SSL_peek() returned error %d, ret = %d\n", err, ret);
if (ret == 0)
return 0;
return -1;
}
}
#endif
gint sock_peek(SockInfo *sock, gchar *buf, gint len)
{
g_return_val_if_fail(sock != NULL, -1);
#if USE_SSL
if (sock->ssl)
return ssl_peek(sock->ssl, buf, len);
#endif
return fd_recv(sock->sock, buf, len, MSG_PEEK);
}
@ -1727,11 +1518,6 @@ gint sock_close(SockInfo *sock)
if (!sock)
return 0;
#if USE_SSL
if (sock->ssl)
ssl_done_socket(sock);
#endif
if (sock->sock_ch) {
g_io_channel_shutdown(sock->sock_ch, FALSE, NULL);
g_io_channel_unref(sock->sock_ch);
@ -1759,13 +1545,4 @@ gint fd_close(gint fd)
#endif
}
#if USE_SSL
void ssl_done_socket(SockInfo *sockinfo)
{
if (sockinfo->ssl) {
SSL_free(sockinfo->ssl);
}
}
#endif
#endif

View file

@ -20,10 +20,6 @@
typedef struct _SockInfo SockInfo;
#if USE_SSL
# include <openssl/ssl.h>
#endif
typedef enum
{
CONN_READY,
@ -42,11 +38,6 @@ typedef gboolean (*SockFunc) (SockInfo *sock,
struct _SockInfo
{
gint sock;
#if USE_SSL
SSL *ssl;
#else
gpointer ssl;
#endif
GIOChannel *sock_ch;
gchar *hostname;
@ -111,15 +102,4 @@ gint fd_gets (gint sock, gchar *buf, gint len);
gint fd_getline (gint sock, gchar **line);
gint fd_close (gint sock);
/* Functions for SSL */
#if USE_SSL
gint ssl_read (SSL *ssl, gchar *buf, gint len);
gint ssl_write (SSL *ssl, const gchar *buf, gint len);
gint ssl_write_all (SSL *ssl, const gchar *buf, gint len);
gint ssl_gets (SSL *ssl, gchar *buf, gint len);
gint ssl_getline (SSL *ssl, gchar **line);
gint ssl_peek (SSL *ssl, gchar *buf, gint len);
void ssl_done_socket (SockInfo *sockinfo);
#endif
#endif /* __SYLPH_SOCKET_H__ */

View file

@ -64,13 +64,15 @@ sokoke_js_script_eval (JSContextRef js_context,
{
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);
JSStringRef js_script = JSStringCreateWithUTF8CString (script);
JSValueRef js_exception = NULL;
JSValueRef js_value = JSEvaluateScript (js_context, js_script,
js_script = JSStringCreateWithUTF8CString (script);
js_value = JSEvaluateScript (js_context, js_script,
JSContextGetGlobalObject (js_context), NULL, 0, &js_exception);
JSStringRelease (js_script);
@ -649,7 +651,7 @@ sokoke_magic_uri (const gchar* uri)
/* Add file:// if we have a local path */
if (g_path_is_absolute (uri))
return g_strconcat ("file://", uri, NULL);
return g_filename_to_uri (uri, NULL, NULL);
/* Parse geo URI geo:48.202778,16.368472;crs=wgs84;u=40 as a location */
if (!strncmp (uri, "geo:", 4))
{
@ -684,7 +686,7 @@ sokoke_magic_uri (const gchar* uri)
search = NULL;
if (!strchr (uri, ' ') &&
((search = strchr (uri, ':')) || (search = strchr (uri, '@'))) &&
search[0] && !g_ascii_isalpha (search[1]))
search[0] && g_ascii_isdigit (search[1]))
return g_strconcat ("http://", uri, NULL);
if ((!strcmp (uri, "localhost") || strchr (uri, '/'))
&& sokoke_resolve_hostname (uri))
@ -793,7 +795,6 @@ sokoke_xfce_header_new (const gchar* icon,
if (sokoke_get_desktop () == SOKOKE_DESKTOP_XFCE)
{
GtkWidget* entry;
GtkStyle* style;
gchar* markup;
GtkWidget* xfce_heading;
GtkWidget* hbox;
@ -804,9 +805,7 @@ sokoke_xfce_header_new (const gchar* icon,
xfce_heading = gtk_event_box_new ();
entry = gtk_entry_new ();
style = gtk_widget_get_style (entry);
gtk_widget_modify_bg (xfce_heading, GTK_STATE_NORMAL,
&style->base[GTK_STATE_NORMAL]);
hbox = gtk_hbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
if (icon)
@ -816,8 +815,6 @@ sokoke_xfce_header_new (const gchar* icon,
GTK_ICON_SIZE_DIALOG);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
label = gtk_label_new (NULL);
gtk_widget_modify_fg (label, GTK_STATE_NORMAL
, &style->text[GTK_STATE_NORMAL]);
markup = g_strdup_printf ("<span size='large' weight='bold'>%s</span>",
title);
gtk_label_set_markup (GTK_LABEL (label), markup);
@ -826,6 +823,16 @@ sokoke_xfce_header_new (const gchar* icon,
g_free (markup);
gtk_widget_destroy (entry);
#if !GTK_CHECK_VERSION (3, 0, 0)
{
GtkStyle* style = gtk_widget_get_style (entry);
gtk_widget_modify_bg (xfce_heading, GTK_STATE_NORMAL,
&style->base[GTK_STATE_NORMAL]);
gtk_widget_modify_fg (label, GTK_STATE_NORMAL
, &style->text[GTK_STATE_NORMAL]);
}
#endif
vbox = gtk_vbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), xfce_heading, FALSE, FALSE, 0);
@ -1029,37 +1036,6 @@ sokoke_time_t_to_julian (const time_t* timestamp)
return julian;
}
/**
* sokoke_days_between:
* @day1: a time_t timestamp value
* @day2: a time_t timestamp value
*
* Calculates the number of days between two timestamps.
*
* Return value: an integer.
**/
gint
sokoke_days_between (const time_t* day1,
const time_t* day2)
{
GDate* date1;
GDate* date2;
gint age;
date1 = g_date_new ();
date2 = g_date_new ();
g_date_set_time_t (date1, *day1);
g_date_set_time_t (date2, *day2);
age = g_date_days_between (date1, date2);
g_date_free (date1);
g_date_free (date2);
return age;
}
/**
* sokoke_set_config_dir:
* @new_config_dir: an absolute path, or %NULL
@ -1731,3 +1707,87 @@ midori_download_prepare_tooltip_text (WebKitDownload* download)
return g_string_free (tooltip, FALSE);
}
static gboolean
sokoke_entry_has_placeholder_text (GtkEntry* entry)
{
const gchar* text = gtk_entry_get_text (entry);
const gchar* hint = gtk_entry_get_placeholder_text (entry);
if (!gtk_widget_has_focus (GTK_WIDGET (entry))
&& hint != NULL
&& (text == NULL || !strcmp (text, hint)))
return TRUE;
return FALSE;
}
static void
sokoke_entry_changed_cb (GtkEditable* editable,
GtkEntry* entry)
{
const gchar* text = gtk_entry_get_text (entry);
gboolean visible = text && *text
&& ! sokoke_entry_has_placeholder_text (entry);
gtk_icon_entry_set_icon_from_stock (
GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_SECONDARY,
visible ? GTK_STOCK_CLEAR : NULL);
}
static gboolean
sokoke_entry_focus_out_event_cb (GtkEditable* editable,
GdkEventFocus* event,
GtkEntry* entry)
{
sokoke_entry_changed_cb (editable, entry);
return FALSE;
}
static void
sokoke_entry_icon_released_cb (GtkEntry* entry,
GtkIconEntryPosition icon_pos,
GdkEvent* event,
gpointer user_data)
{
if (icon_pos != GTK_ICON_ENTRY_SECONDARY)
return;
gtk_entry_set_text (entry, "");
gtk_widget_grab_focus (GTK_WIDGET (entry));
}
void
sokoke_entry_set_clear_button_visible (GtkEntry* entry,
gboolean visible)
{
g_return_if_fail (GTK_IS_ENTRY (entry));
gtk_icon_entry_set_icon_highlight (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_SECONDARY, TRUE);
if (visible)
{
g_object_connect (entry,
"signal::icon-release",
G_CALLBACK (sokoke_entry_icon_released_cb), NULL,
"signal::focus-in-event",
G_CALLBACK (sokoke_entry_focus_out_event_cb), entry,
"signal::focus-out-event",
G_CALLBACK (sokoke_entry_focus_out_event_cb), entry,
"signal::changed",
G_CALLBACK (sokoke_entry_changed_cb), entry, NULL);
sokoke_entry_changed_cb ((GtkEditable*)entry, entry);
}
else
{
g_object_disconnect (entry,
"any_signal::icon-release",
G_CALLBACK (sokoke_entry_icon_released_cb), NULL,
"any_signal::focus-in-event",
G_CALLBACK (sokoke_entry_focus_out_event_cb), entry,
"any_signal::focus-out-event",
G_CALLBACK (sokoke_entry_focus_out_event_cb), entry,
"any_signal::changed",
G_CALLBACK (sokoke_entry_changed_cb), entry, NULL);
gtk_icon_entry_set_icon_from_stock (
GTK_ICON_ENTRY (entry), GTK_ICON_ENTRY_SECONDARY, NULL);
}
}

View file

@ -121,10 +121,6 @@ sokoke_action_create_popup_menu_item (GtkAction* action);
gint64
sokoke_time_t_to_julian (const time_t* timestamp);
gint
sokoke_days_between (const time_t* day1,
const time_t* day2);
const gchar*
sokoke_set_config_dir (const gchar* new_config_dir);
@ -202,4 +198,8 @@ sokoke_build_thumbnail_path (const gchar* name);
gchar*
midori_download_prepare_tooltip_text (WebKitDownload* download);
void
sokoke_entry_set_clear_button_visible (GtkEntry* entry,
gboolean visible);
#endif /* !__SOKOKE_H__ */

View file

@ -7,7 +7,7 @@ import platform
progressive = True
libs = 'M UNIQUE LIBSOUP GMODULE GTHREAD LIBIDN GIO GTK SQLITE ' \
'LIBNOTIFY WEBKIT JAVASCRIPTCOREGTK LIBXML X11 XSS WS2_32 OPENSSL HILDON' \
'LIBNOTIFY WEBKIT JAVASCRIPTCOREGTK LIBXML X11 XSS WS2_32 HILDON' \
'HILDON_FM'
if progressive or Options.commands['check']:

View file

@ -925,11 +925,7 @@ midori_bookmarks_init (MidoriBookmarks* bookmarks)
gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_PRIMARY,
GTK_STOCK_FIND);
gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_SECONDARY,
GTK_STOCK_CLEAR);
gtk_icon_entry_set_icon_highlight (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_SECONDARY, TRUE);
sokoke_entry_set_clear_button_visible (GTK_ENTRY (entry), TRUE);
g_signal_connect (entry, "icon-release",
G_CALLBACK (midori_bookmarks_filter_entry_clear_cb), bookmarks);
g_signal_connect (entry, "changed",

View file

@ -116,18 +116,59 @@ midori_history_get_stock_id (MidoriViewable* viewable)
return STOCK_HISTORY;
}
#if !GLIB_CHECK_VERSION (2, 26, 0)
static gint
sokoke_days_between (const time_t* day1,
const time_t* day2)
{
GDate* date1;
GDate* date2;
gint age;
date1 = g_date_new ();
date2 = g_date_new ();
g_date_set_time_t (date1, *day1);
g_date_set_time_t (date2, *day2);
age = g_date_days_between (date1, date2);
g_date_free (date1);
g_date_free (date2);
return age;
}
#endif
static gchar*
midori_history_format_date (KatzeItem *item)
{
gint age;
gint64 day;
gchar token[50];
gint64 day = katze_item_get_added (item);
gchar* sdate;
gint age;
#if GLIB_CHECK_VERSION (2, 26, 0)
GDateTime* now = g_date_time_new_now_local ();
GDateTime* then = g_date_time_new_from_unix_local (day);
age = g_date_time_get_day_of_year (now) - g_date_time_get_day_of_year (then);
if (g_date_time_get_year (now) != g_date_time_get_year (then))
age = 999;
if (age == 0)
sdate = g_strdup (_("Today"));
else if (age == 1)
sdate = g_strdup (_("Yesterday"));
else if (age < 7)
sdate = g_strdup_printf (ngettext ("%d day ago",
"%d days ago", (gint)age), (gint)age);
else if (age == 7)
sdate = g_strdup (_("A week ago"));
else
sdate = g_date_time_format (then, "%x");
#else
gchar token[50];
time_t current_time;
current_time = time (NULL);
day = katze_item_get_added (item);
age = sokoke_days_between ((time_t*)&day, &current_time);
/* A negative age is a date in the future, the clock is probably off */
@ -147,6 +188,7 @@ midori_history_format_date (KatzeItem *item)
sdate = g_strdup (_("Today"));
else
sdate = g_strdup (_("Yesterday"));
#endif
return sdate;
}
@ -412,9 +454,8 @@ midori_history_add_item_cb (KatzeArray* array,
GtkTreeModel* model = gtk_tree_view_get_model (treeview);
GtkTreeIter iter;
KatzeItem* today;
time_t current_time;
time_t current_time = time (NULL);
current_time = time (NULL);
if (gtk_tree_model_iter_children (model, &iter, NULL))
{
gint64 day;
@ -423,7 +464,18 @@ midori_history_add_item_cb (KatzeArray* array,
gtk_tree_model_get (model, &iter, 0, &today, -1);
day = katze_item_get_added (today);
#if GLIB_CHECK_VERSION (2, 26, 0)
has_today = g_date_time_get_day_of_month (
g_date_time_new_from_unix_local (day))
== g_date_time_get_day_of_month (
g_date_time_new_from_unix_local (current_time))
&& g_date_time_get_day_of_year (
g_date_time_new_from_unix_local (day))
== g_date_time_get_day_of_year (
g_date_time_new_from_unix_local (current_time));
#else
has_today = sokoke_days_between ((time_t*)&day, &current_time) == 0;
#endif
g_object_unref (today);
if (has_today)
{
@ -945,12 +997,7 @@ midori_history_init (MidoriHistory* history)
gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_PRIMARY,
GTK_STOCK_FIND);
gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_SECONDARY,
GTK_STOCK_CLEAR);
gtk_icon_entry_set_icon_highlight (GTK_ICON_ENTRY (entry),
GTK_ICON_ENTRY_SECONDARY,
TRUE);
sokoke_entry_set_clear_button_visible (GTK_ENTRY (entry), TRUE);
g_signal_connect (entry, "icon-release",
G_CALLBACK (midori_history_filter_entry_clear_cb), history);
g_signal_connect (entry, "changed",

View file

@ -39,7 +39,9 @@ extensions/feed-panel/feed-panel.c
extensions/feed-panel/feed-parse.c
extensions/feed-panel/feed-rss.c
extensions/feed-panel/main.c
extensions/formhistory.c
extensions/formhistory/formhistory.c
extensions/formhistory/formhistory-gdom-frontend.c
extensions/formhistory/formhistory-js-frontend.c
extensions/history-list.vala
extensions/mouse-gestures.c
extensions/shortcuts.c

2207
po/ar.po

File diff suppressed because it is too large Load diff

2032
po/ca.po

File diff suppressed because it is too large Load diff

1721
po/cs.po

File diff suppressed because it is too large Load diff

1008
po/da.po

File diff suppressed because it is too large Load diff

1483
po/de.po

File diff suppressed because it is too large Load diff

2793
po/eo.po Normal file

File diff suppressed because it is too large Load diff

1546
po/es.po

File diff suppressed because it is too large Load diff

1481
po/he.po

File diff suppressed because it is too large Load diff

2656
po/hu.po

File diff suppressed because it is too large Load diff

2073
po/id.po

File diff suppressed because it is too large Load diff

2225
po/it.po

File diff suppressed because it is too large Load diff

1307
po/ja.po

File diff suppressed because it is too large Load diff

1673
po/ko.po

File diff suppressed because it is too large Load diff

2749
po/lt.po

File diff suppressed because it is too large Load diff

1193
po/nl.po

File diff suppressed because it is too large Load diff

1000
po/pl.po

File diff suppressed because it is too large Load diff

1612
po/pt.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1460
po/ru.po

File diff suppressed because it is too large Load diff

1206
po/sk.po

File diff suppressed because it is too large Load diff

2100
po/tr.po

File diff suppressed because it is too large Load diff

1270
po/uk.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -94,6 +94,38 @@ browser_tooltips (void)
g_error ("Tooltip errors");
}
static void
browser_site_data (void)
{
typedef struct
{
const gchar* url;
MidoriSiteDataPolicy policy;
} PolicyItem;
static const PolicyItem items[] = {
{ "google.com", MIDORI_SITE_DATA_BLOCK },
{ "facebook.com", MIDORI_SITE_DATA_BLOCK },
{ "bugzilla.gnome.org", MIDORI_SITE_DATA_PRESERVE },
{ "bugs.launchpad.net", MIDORI_SITE_DATA_ACCEPT },
};
const gchar* rules = "-google.com,-facebook.com,!bugzilla.gnome.org,+bugs.launchpad.net";
MidoriWebSettings* settings = g_object_new (MIDORI_TYPE_WEB_SETTINGS,
"site-data-rules", rules, NULL);
guint i;
for (i = 0; i < G_N_ELEMENTS (items); i++)
{
MidoriSiteDataPolicy policy = midori_web_settings_get_site_data_policy (
settings, items[i].url);
if (policy != items[i].policy)
g_error ("Match '%s' yields %d but %d expected",
items[i].url, policy, items[i].policy);
}
g_object_unref (settings);
}
int
main (int argc,
char** argv)
@ -106,6 +138,7 @@ main (int argc,
g_test_add_func ("/browser/create", browser_create);
g_test_add_func ("/browser/tooltips", browser_tooltips);
g_test_add_func ("/browser/site_data", browser_site_data);
return g_test_run ();
}

View file

@ -13,6 +13,7 @@
#include <midori/midori.h>
#define SM "http://www.searchmash.com/search/"
#define HTTP_PREFIX "midori-unit-test-expected-http-prefix"
static void
test_input (const gchar* input,
@ -20,6 +21,7 @@ test_input (const gchar* input,
{
static KatzeArray* search_engines = NULL;
gchar* uri;
gchar* real_expected = NULL;
if (G_UNLIKELY (!search_engines))
{
@ -41,28 +43,29 @@ test_input (const gchar* input,
uri = sokoke_magic_uri (input);
if (!uri)
{
gchar** parts;
gchar* keywords = NULL;
const gchar* keywords = NULL;
const gchar* search_uri = NULL;
KatzeItem* item;
/* Do we have a keyword and a string? */
parts = g_strsplit (input, " ", 2);
if (parts[0])
if ((item = katze_array_find_token (search_engines, input)))
{
KatzeItem* item;
if ((item = katze_array_find_token (search_engines, parts[0])))
{
keywords = g_strdup (parts[1] ? parts[1] : "");
keywords = strchr (input, ' ');
if (keywords != NULL)
keywords++;
else
keywords = "";
search_uri = katze_item_get_uri (item);
}
}
g_strfreev (parts);
uri = keywords ? midori_uri_for_search (search_uri, keywords) : NULL;
g_free (keywords);
uri = search_uri ? midori_uri_for_search (search_uri, keywords) : NULL;
}
katze_assert_str_equal (input, uri, expected);
if (!g_strcmp0 (expected, HTTP_PREFIX))
real_expected = g_strconcat ("http://", input, NULL);
katze_assert_str_equal (input, uri, real_expected ? real_expected : expected);
g_free (real_expected);
g_free (uri);
}
@ -97,6 +100,13 @@ magic_uri_uri (void)
/* test_input ("foo:f1o2o3@bar.baz", "http://f1o2o3:foo@bar.baz"); */
/* test_input ("foo:foo@bar.baz", "http://foo:foo@bar.baz"); */
test_input ("2001:0db8:85a3:0000:0000:8a2e:0370:7334", HTTP_PREFIX);
test_input ("fe80:0:0:0:202:b3ff:fe1e:8329", HTTP_PREFIX);
test_input ("fe80::202:b3ff:fe1e:8329", HTTP_PREFIX);
test_input ("fe80::76e5:bff:fe04:38e0/64", HTTP_PREFIX);
test_input ("content::browser", NULL);
test_input ("std::copy", NULL);
uri = "http://bugs.launchpad.net/midori";
g_assert_cmpstr ("bugs.launchpad.net", ==, midori_uri_parse_hostname (uri, NULL));
uri = "https://bugs.launchpad.net/midori";

View file

@ -154,13 +154,13 @@ properties_object_get_set (GObject* object)
static void
properties_object_test (gconstpointer object)
{
if (GTK_IS_OBJECT (object))
if (GTK_IS_WIDGET (object))
g_object_ref_sink ((GObject*)object);
properties_object_get_set ((GObject*)object);
if (GTK_IS_OBJECT (object))
gtk_object_destroy (GTK_OBJECT (object));
if (GTK_IS_WIDGET (object))
gtk_widget_destroy (GTK_WIDGET (object));
g_object_unref ((GObject*)object);
}

View file

@ -112,7 +112,6 @@ midori_findbar_entry_clear_icon_released_cb (GtkIconEntry* entry,
{
if (icon_pos == GTK_ICON_ENTRY_SECONDARY)
{
gtk_entry_set_text (GTK_ENTRY (entry), "");
midori_findbar_set_icon (findbar, GTK_ICON_ENTRY_PRIMARY, "edit-find");
}
}
@ -265,8 +264,7 @@ midori_findbar_init (MidoriFindbar* findbar)
gtk_toolbar_insert (GTK_TOOLBAR (findbar), toolitem, -1);
findbar->find_text = gtk_icon_entry_new ();
midori_findbar_set_icon (findbar, GTK_ICON_ENTRY_PRIMARY, "edit-find");
gtk_icon_entry_set_icon_highlight (GTK_ICON_ENTRY (findbar->find_text),
GTK_ICON_ENTRY_SECONDARY, TRUE);
sokoke_entry_set_clear_button_visible (GTK_ENTRY (findbar->find_text), TRUE);
g_signal_connect (findbar->find_text, "icon-release",
G_CALLBACK (midori_findbar_entry_clear_icon_released_cb), findbar);
g_signal_connect (findbar->find_text, "activate",

View file

@ -1,6 +1,7 @@
#! /bin/sh
# Copyright (C) 2010-2011 Peter de Ridder <peter@xfce.org>
# Copyright (C) 2012 Paweł Forysiuk <tuxator@o2.pl>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -23,17 +24,30 @@
# a bit of configuration
root_dir=$MINGW_PREFIX
if [ "$MINGW_PREFIX" == "" ]; then
echo "Warning! MINGW_PREFIX variable is empty!"
sleep 5s
fi
# create temporary working directory
temp_dir=`mktemp -d`
# check if we can use 7zip
have_7zip=`which 7za`
if [ "$1" != "" ]; then
if [ "$1" == "debug" ]; then
DEBUG_BUILD=1
else
version_tag=$1
fi
fi
# generate unique filename
if [ "$have_7zip" != "" ]; then
ARCHIVE=midori$1-`date +%Y%m%d%H%M`.7z
ARCHIVE=midori$version_tag-`date +%Y%m%d%H%M`.7z
else
ARCHIVE=midori$1-`date +%Y%m%d%H%M`.zip
ARCHIVE=midori$version_tag-`date +%Y%m%d%H%M`.zip
fi
# function: dll-recursive <list of exe and dll files ...>
@ -55,7 +69,7 @@ dll_recursive ()
while [ "x`sha1sum - < $temp_file_new`" != "x`sha1sum - < $temp_file_old`" ]
do
files=`cat $temp_file_new $temp_file_old | sort | uniq -u`
cp $temp_file_new $temp_file_old
cp -L $temp_file_new $temp_file_old
strings $files 2> /dev/null | grep \\.dll | cat - $temp_file_old | sort | uniq > $temp_file_new
done
@ -64,16 +78,36 @@ dll_recursive ()
rm $temp_file_new $temp_file_old
}
grab_files ()
{
local dir="$1"
pushd $root_dir > /dev/null
shift
while [ "$1" ]; do
find $dir "(" -name "$1" ")" -prune -exec mkdir -p $workdir/{} ";" -exec rmdir --ignore-fail-on-non-empty $workdir/{} ";" -exec cp -Lr {} $workdir/{} ";"
shift
done
popd > /dev/null
}
echo -n "Creating $ARCHIVE ."
# create destination folder
mkdir $temp_dir/midori$1
workdir=$temp_dir/midori$version_tag
mkdir $workdir
echo -n .
# auto generate dll list, only of existing files
pushd $root_dir/bin > /dev/null
dll_recursive midori*.exe gspawn-*-helper*.exe libhunspell*.dll > $temp_dir/midori.exe.lst
dll_recursive ../lib/gio/modules/*.dll >> $temp_dir/midori.exe.lst
dll_recursive iconv.dll >> $temp_dir/midori.exe.lst
if [ "$DEBUG_BUILD" != "" ]; then
dll_recursive gdb.exe GtkLauncher.exe >> $temp_dir/midori.exe.lst
fi
files=`ls | cat - $temp_dir/midori.exe.lst | sort | uniq -d`
rm $temp_dir/midori.exe.lst
popd > /dev/null
@ -82,46 +116,80 @@ echo -n .
# copy auto generate dll list
pushd $root_dir/bin > /dev/null
mkdir $temp_dir/midori$1/bin
cp $files $temp_dir/midori$1/bin
mkdir $workdir/bin
cp -L $files $workdir/bin
popd > /dev/null
echo -n .
# copy etc
pushd $root_dir > /dev/null
find etc "(" -name "*midori*" -o -name "gtkrc" ")" -prune -exec mkdir -p $temp_dir/midori$1/{} ";" -exec rmdir --ignore-fail-on-non-empty $temp_dir/midori$1/{} ";" -exec cp -r {} $temp_dir/midori$1/{} ";"
popd > /dev/null
grab_files etc midori
grab_files etc gtkrc
# If modules are not compiled we need a list of them
grab_files etc pango
# Freetype is preferred font backend
# copy configuration, otherwise webkit will crash
grab_files etc fonts
echo -n .
# copy lib
pushd $root_dir > /dev/null
find lib "(" -path "lib/midori/*" -o -path "lib/gtk-2.0/*" -o -path "lib/enchant/*" -o -path "lib/engines/*" ")" -a -name "*.dll" -prune -exec mkdir -p $temp_dir/midori$1/{} ";" -exec rmdir --ignore-fail-on-non-empty $temp_dir/midori$1/{} ";" -exec cp -r {} $temp_dir/midori$1/{} ";"
popd > /dev/null
grab_files lib midori
grab_files lib gtk-2.0
grab_files lib/engines "*"
grab_files lib gdk-pixbuf-2.0
grab_files lib enchant
grab_files lib gio
# Fedora ships on-demand pango modules, check just in case
grab_files lib pango
echo -n .
# copy share
pushd $root_dir > /dev/null
find share "(" -name "*midori*" -o -name "icons" -o -name "MS-Windows" -o -name "mime" ")" -prune -exec mkdir -p $temp_dir/midori$1/{} ";" -exec rmdir --ignore-fail-on-non-empty $temp_dir/midori$1/{} ";" -exec cp -r {} $temp_dir/midori$1/{} ";"
grab_files share midori
grab_files share icons
grab_files share MS-Windows
grab_files share mime
grab_files share midori.mo
grab_files share webkitgtk-1.0
if [ "$DEBUG_BUILD" == "" ];then
pushd $workdir > /dev/null
find -iname *.debug -exec rm {} \;
popd > /dev/null
fi
# copy locales for gtk
# so we have translated stock items, file dialogs
find share "(" -name "midori.mo" ")" > locale.list
mkdir -p $temp_dir/midori$1/share/locale/
for LOCALE in $(cat locale.list); do
pushd $root_dir > /dev/null
find share "(" -name "midori.mo" ")" > $temp_dir/locale.list
mkdir -p $workdir/share/locale/
for LOCALE in $(cat $temp_dir/locale.list); do
LOCALE=$(echo $LOCALE|awk -F/ '{print $3}')
cp /usr/share/locale/$LOCALE/LC_MESSAGES/gtk20.mo $temp_dir/midori$1/share/locale/$LOCALE/LC_MESSAGES/
cp /usr/share/locale/$LOCALE/LC_MESSAGES/gtk20.mo $workdir/share/locale/$LOCALE/LC_MESSAGES/
done
rm locale.list
rm $temp_dir/locale.list
# Use small icons and tango icons
gtk_etc_dir="$workdir/etc/gtk-2.0/"
mkdir -p $gtk_etc_dir
cat > $gtk_etc_dir/gtkrc << _EOF
gtk-theme-name = "MS-Windows"
gtk-fallback-icon-theme = "Tango"
gtk-toolbar-style = GTK_TOOLBAR_ICONS
_EOF
popd > /dev/null
echo -n .
# copy doc files to root
cp $temp_dir/midori$1/share/doc/midori/{COPYING,AUTHORS} $temp_dir/midori$1
cp -L $workdir/share/doc/midori/{COPYING,AUTHORS} $workdir
echo -n .
@ -130,9 +198,9 @@ ARCHIVE=`pwd`/$ARCHIVE
# store as zip/7z file
pushd $temp_dir > /dev/null
if [ "$have_7zip" != "" ]; then
7za a -m0=lzma -mx=9 $ARCHIVE midori$1
7za a -m0=lzma $ARCHIVE midori$version_tag
else
zip -rq $ARCHIVE midori$1
zip -rq $ARCHIVE midori$version_tag
fi
popd > /dev/null

15
wscript
View file

@ -29,7 +29,7 @@ from Configure import find_program_impl
major = 0
minor = 4
micro = 3
micro = 4
APPNAME = 'midori'
VERSION = VERSION_FULL = str (major) + '.' + str (minor) + '.' + str (micro)
@ -62,6 +62,9 @@ def is_mingw (env):
return cc.find ('mingw') != -1# or cc.find ('wine') != -1
return False
def is_win32 (env):
return is_mingw (env) or Options.platform == 'win32'
# Compile Win32 res files to (resource) object files
def rc_file(self, node):
rctask = self.create_task ('winrc')
@ -109,7 +112,7 @@ def configure (conf):
else:
icons = 'no '
if is_mingw (conf.env) or Options.platform == 'win32':
if is_win32 (conf.env):
conf.find_program ('windres', var='WINRC')
conf.env['platform'] = 'win32'
@ -271,8 +274,6 @@ def configure (conf):
if not conf.env['HAVE_UNIQUE']:
if Options.platform == 'win32':
conf.check (lib='ws2_32')
check_pkg ('openssl', mandatory=False)
conf.define ('USE_SSL', [0,1][conf.env['HAVE_OPENSSL'] == 1])
conf.define ('HAVE_NETDB_H', [0,1][conf.check (header_name='netdb.h')])
conf.check (header_name='sys/wait.h')
conf.check (header_name='sys/select.h')
@ -391,7 +392,7 @@ def set_options (opt):
add_enable_option ('apidocs', 'API documentation', group, disable=True)
group = opt.add_option_group ('Optional features', '')
add_enable_option ('unique', 'single instance support', group)
add_enable_option ('unique', 'single instance support', group, disable=is_win32 (os.environ))
add_enable_option ('libnotify', 'notification support', group)
add_enable_option ('addons', 'building of extensions', group)
add_enable_option ('tests', 'building of tests', group, disable=True)
@ -452,7 +453,7 @@ def build (bld):
bld.install_files ('${DOCDIR}/api/', blddir + '/docs/api/*')
for desktop in [APPNAME + '.desktop', APPNAME + '-private.desktop']:
if is_mingw (bld.env) or Options.platform == 'win32':
if is_win32 (bld.env):
break
if bld.env['HAVE_HILDON']:
appdir = '${MDATADIR}/applications/hildon'
@ -497,7 +498,7 @@ def build (bld):
else:
Utils.pprint ('BLUE', "logo-shade could not be rasterized.")
for res_file in ['error.html', 'close.png']:
for res_file in ['about.css', 'error.html', 'close.png']:
bld.install_files ('${MDATADIR}/' + APPNAME + '/res', 'data/' + res_file)
bld.install_as ( \
'${MDATADIR}/' + APPNAME + '/res/speeddial-head-%s.html' % VERSION, \