about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorMarco Peereboom <marco@conformal.com>2011-12-28 13:38:37 -0600
committerMarco Peereboom <marco@conformal.com>2011-12-28 13:41:23 -0600
commit748728177d5afa65af189ca1b50523a6b0d431e3 (patch)
treee36688de737ab6140c28ac1b7c877320792fdb98
parentf6a3ffaf8264b9c7d5bfa2747fce9bdd5531dc1d (diff)
downloadxombrero-748728177d5afa65af189ca1b50523a6b0d431e3.tar.gz
Add link coloring after visiting it.
Work around for bug #51747
from Stevan Andjelkovic <stevan.andjelkovic@strath.ac.uk>
-rw-r--r--about.c5
-rw-r--r--settings.c2
-rw-r--r--xxxterm.117
-rw-r--r--xxxterm.c230
-rw-r--r--xxxterm.conf1
-rw-r--r--xxxterm.h10
6 files changed, 209 insertions, 56 deletions
diff --git a/about.c b/about.c
index 744a870..3df1795 100644
--- a/about.c
+++ b/about.c
@@ -1265,7 +1265,7 @@ xtp_page_hl(struct tab *t, struct karg *args)
 
 	/* body */
 	body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
-	    "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
+	    "<th>URI</th><th>Title</th><th>Last visited</th><th style='width: 40px'>Rm</th></tr>\n");
 
 	RB_FOREACH_REVERSE(h, history_list, &hl) {
 		tmp = body;
@@ -1273,9 +1273,10 @@ xtp_page_hl(struct tab *t, struct karg *args)
 		    "%s\n<tr>"
 		    "<td><a href='%s'>%s</a></td>"
 		    "<td>%s</td>"
+		    "<td>%s</td>"
 		    "<td style='text-align: center'>"
 		    "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
-		    body, h->uri, h->uri, h->title,
+		    body, h->uri, h->uri, h->title, ctime(&h->time),
 		    XT_XTP_STR, XT_XTP_HL, hl_session_key,
 		    XT_XTP_HL_REMOVE, i);
 
diff --git a/settings.c b/settings.c
index d1de255..04d8e91 100644
--- a/settings.c
+++ b/settings.c
@@ -68,6 +68,7 @@ char		*http_proxy = NULL;
 char		download_dir[PATH_MAX];
 char		runtime_settings[PATH_MAX]; /* override of settings */
 int		allow_volatile_cookies = 0;
+int		color_visited_uris = 1;
 int		save_global_history = 0; /* save global history to disk */
 char		*user_agent = NULL;
 int		save_rejected_cookies = 0;
@@ -243,6 +244,7 @@ struct settings		rs[] = {
 	{ "append_next",		XT_S_INT, 0,		&append_next, NULL, NULL },
 	{ "autofocus_onload",		XT_S_INT, 0,		&autofocus_onload, NULL, NULL },
 	{ "browser_mode",		XT_S_INT, 0, NULL, NULL,&s_browser_mode },
+	{ "color_visited_uris",		XT_S_INT, 0,		&color_visited_uris, NULL, NULL },
 	{ "cookie_policy",		XT_S_INT, 0, NULL, NULL,&s_cookie },
 	{ "cookies_enabled",		XT_S_INT, 0,		&cookies_enabled, NULL, NULL },
 	{ "ctrl_click_focus",		XT_S_INT, 0,		&ctrl_click_focus, NULL, NULL },
diff --git a/xxxterm.1 b/xxxterm.1
index fdc0643..4e33ae2 100644
--- a/xxxterm.1
+++ b/xxxterm.1
@@ -940,6 +940,15 @@ See the default config file for more details.
 Set the command prompt font.
 E.g.
 .Pa cmd_font = monospace normal 9 .
+.It Cm color_visited_uris
+When enabled (the default)
+.Nm
+will color visited links. This is done while the web page loads using
+JavaScript, rather than WebKit's (broken, see bug #51747) built-in
+facility for coloring visited links. The JavaScript approach is
+(probably) slower and is not consistent across tabs (unless the tabs are
+reloaded), but has the advantage of not leaking history data to web
+pages (see http://wtikay.com/docs/details.html).
 .It Cm cookie_policy
 This field delineates the cookie policy.
 Possible values are: no3rdparty, reject 3rd party cookies.
@@ -1235,11 +1244,3 @@ was written by
 .An Raphael Graf Aq r@undefined.ch ,
 and
 .An Michal Mazurek Aq akfaew@jasminek.net .
-.Sh BUGS
-When
-.Cm save_global_history
-is enabled
-.Nm
-is supposed to, in addition to restoring the global history, color the
-visited links accordingly; however due to bug #51747 in WebKit this does
-not happen.
diff --git a/xxxterm.c b/xxxterm.c
index bf2198c..fddf269 100644
--- a/xxxterm.c
+++ b/xxxterm.c
@@ -23,8 +23,9 @@
 
 char		*version = XXXTERM_VERSION;
 
-#ifdef XT_DEBUG
+/*#ifdef XT_DEBUG*/
 u_int32_t		swm_debug = 0
+/*
 			    | XT_D_MOVE
 			    | XT_D_KEY
 			    | XT_D_TAB
@@ -41,6 +42,9 @@ u_int32_t		swm_debug = 0
 			    | XT_D_CLIP
 			    | XT_D_BUFFERCMD
 			    | XT_D_INSPECTOR
+*/
+			    | XT_D_VISITED
+			    | XT_D_HISTORY
 			    ;
 #endif
 
@@ -95,6 +99,11 @@ TAILQ_HEAD(command_list, command_entry);
 #define XT_DLMAN_REFRESH	"10"
 #define XT_MAX_URL_LENGTH	(4096) /* 1 page is atomic, don't make bigger */
 #define XT_MAX_UNDO_CLOSE_TAB	(32)
+#define XT_MAX_HL_PURGE_COUNT	(1000) /* Purge the history for every
+					* MAX_HL_PURGE_COUNT items inserted into
+					* history and delete all items older
+					* than MAX_HISTORY_AGE. */
+#define XT_MAX_HISTORY_AGE	(60.0 * 60.0 * 24 * 14) /* 14 days */
 #define XT_RESERVED_CHARS	"$&+,/:;=?@ \"<>#%%{}|^~[]`"
 #define XT_PRINT_EXTRA_MARGIN	10
 #define XT_URL_REGEX		("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
@@ -223,6 +232,7 @@ GtkWidget		*tab_bar;
 GtkWidget		*arrow, *abtn;
 struct tab_list		tabs;
 struct history_list	hl;
+int			hl_purge_count = 0;
 struct session_list	sessions;
 struct domain_list	c_wl;
 struct domain_list	js_wl;
@@ -828,7 +838,7 @@ get_uri(struct tab *t)
 	const gchar		*uri = NULL;
 
 	if (webkit_web_view_get_load_status(t->wv) == WEBKIT_LOAD_FAILED)
-		return NULL;
+		return (NULL);
 	if (t->xtp_meaning == XT_XTP_TAB_MEANING_NORMAL) {
 		uri = webkit_web_view_get_uri(t->wv);
 	} else {
@@ -1048,18 +1058,75 @@ userstyle(struct tab *t, struct karg *args)
 	return (0);
 }
 
-/*
- * Doesn't work fully, due to the following bug:
- * https://bugs.webkit.org/show_bug.cgi?id=51747
- */
+int
+purge_history(void)
+{
+	struct history		*h, *next;
+	double			age = 0.0;
+
+	DNPRINTF(XT_D_HISTORY, "%s: hl_purge_count = %d (%d is max)\n",
+	    __func__, hl_purge_count, XT_MAX_HL_PURGE_COUNT);
+
+	if (hl_purge_count == XT_MAX_HL_PURGE_COUNT) {
+		hl_purge_count = 0;
+
+		for (h = RB_MIN(history_list, &hl); h != NULL; h = next) {
+
+			next = RB_NEXT(history_list, &hl, h);
+
+			age = difftime(time(NULL), h->time);
+
+			if (age > XT_MAX_HISTORY_AGE) {
+				DNPRINTF(XT_D_HISTORY, "%s: removing %s (age %.1f)\n",
+				    __func__, h->uri, age);
+
+				RB_REMOVE(history_list, &hl, h);
+				g_free(h->uri);
+				g_free(h->title);
+				g_free(h);
+			} else {
+				DNPRINTF(XT_D_HISTORY, "%s: keeping %s (age %.1f)\n",
+				    __func__, h->uri, age);
+			}
+		}
+	}
+
+	return (0);
+}
+
+int
+insert_history_item(const gchar *uri, const gchar *title, time_t time)
+{
+	struct history		*h;
+
+	if (!(uri && strlen(uri) && title && strlen(title)))
+		return (1);
+
+	h = g_malloc(sizeof(struct history));
+	h->uri = g_strdup(uri);
+	h->title = g_strdup(title);
+	h->time = time;
+
+	DNPRINTF(XT_D_HISTORY, "%s: adding %s\n", __func__, h->uri);
+
+	RB_INSERT(history_list, &hl, h);
+	completion_add_uri(h->uri);
+	hl_purge_count++;
+
+	purge_history();
+	update_history_tabs(NULL);
+
+	return (0);
+}
+
 int
 restore_global_history(void)
 {
 	char			file[PATH_MAX];
 	FILE			*f;
-	struct history		*h;
-	gchar			*uri;
-	gchar			*title;
+	gchar			*uri, *title, *stime, *err = NULL;
+	time_t			time;
+	struct tm		tm;
 	const char		delim[3] = {'\\', '\\', '\0'};
 
 	snprintf(file, sizeof file, "%s/%s", work_dir, XT_HISTORY_FILE);
@@ -1076,29 +1143,44 @@ restore_global_history(void)
 
 		if ((title = fparseln(f, NULL, NULL, delim, 0)) == NULL)
 			if (feof(f) || ferror(f)) {
-				free(uri);
-				warnx("%s: broken history file\n", __func__);
-				return (1);
+				err = "broken history file (title)";
+				goto done;
 			}
 
-		if (uri && strlen(uri) && title && strlen(title)) {
-			webkit_web_history_item_new_with_data(uri, title);
-			h = g_malloc(sizeof(struct history));
-			h->uri = g_strdup(uri);
-			h->title = g_strdup(title);
-			RB_INSERT(history_list, &hl, h);
-			completion_add_uri(h->uri);
-		} else {
-			warnx("%s: failed to restore history\n", __func__);
-			free(uri);
-			free(title);
-			return (1);
+		if ((stime = fparseln(f, NULL, NULL, delim, 0)) == NULL)
+			if (feof(f) || ferror(f)) {
+				err = "broken history file (time)";
+				goto done;
+			}
+
+		if (strptime(stime, "%a %b %d %H:%M:%S %Y", &tm) == NULL) {
+			err = "strptime failed to parse time";
+			goto done;
+		}
+
+		time = mktime(&tm);
+
+		if (insert_history_item(uri, title, time)) {
+			err = "failed to insert item";
+			goto done;
 		}
 
 		free(uri);
 		free(title);
+		free(stime);
 		uri = NULL;
 		title = NULL;
+		stime = NULL;
+	}
+
+done:
+	if (err && strlen(err)) {
+		warnx("%s: %s\n", __func__, err);
+		free(uri);
+		free(title);
+		free(stime);
+
+		return (1);
 	}
 
 	return (0);
@@ -1120,8 +1202,9 @@ save_global_history_to_disk(struct tab *t)
 	}
 
 	RB_FOREACH_REVERSE(h, history_list, &hl) {
-		if (h->uri && h->title)
-			fprintf(f, "%s\n%s\n", h->uri, h->title);
+		if (h->uri && h->title && h->time)
+			fprintf(f, "%s\n%s\n%s", h->uri, h->title,
+			    ctime(&h->time));
 	}
 
 	fclose(f);
@@ -1311,6 +1394,62 @@ save_tabs_and_quit(struct tab *t, struct karg *args)
 	return (1);
 }
 
+/* Marshall the internal record of visited URIs into a Javascript hash table in
+ * string form. */
+char *
+color_visited_helper(void)
+{
+	char			*s = NULL;
+	struct history		*h;
+
+	RB_FOREACH_REVERSE(h, history_list, &hl) {
+		if (s == NULL)
+			s = g_strdup_printf("'%s':'dummy'", h->uri);
+		else
+			s = g_strjoin(",", s,
+			    g_strdup_printf("'%s':'dummy'", h->uri), NULL);
+	}
+
+	s = g_strdup_printf("{%s}", s);
+
+	DNPRINTF(XT_D_VISITED, "%s: s = %s\n", __func__, s);
+
+	return (s);
+}
+
+int
+color_visited(struct tab *t, char *visited)
+{
+	char		*s;
+
+	if (t == NULL || visited == NULL) {
+		show_oops(NULL, "%s: invalid parameters", __func__);
+		return (1);
+	}
+
+	/* Create a string representing an annonymous Javascript function, which
+	 * takes a hash table of visited URIs as an argument, goes through the
+	 * links at the current web page and colors them if they indeed been
+	 * visited.
+	 */
+	s = g_strconcat(
+	    "(function(visitedUris) {",
+	    "    for (var i = 0; i < document.links.length; i++)",
+	    "        if (visitedUris[document.links[i].href])",
+	    "            document.links[i].style.color = 'purple';",
+	    "})",
+	    /* Apply the annonymous function to the hash table containing
+	     * visited URIs. */
+	    g_strdup_printf("(%s);", visited),
+	    NULL);
+
+	run_script(t, s);
+	g_free(s);
+	g_free(visited);
+
+	return (0);
+}
+
 int
 run_page_script(struct tab *t, struct karg *args)
 {
@@ -3738,7 +3877,7 @@ notify_icon_loaded_cb(WebKitWebView *wv, gchar *uri, struct tab *t)
 void
 notify_load_status_cb(WebKitWebView* wview, GParamSpec* pspec, struct tab *t)
 {
-	const gchar		*uri = NULL, *title = NULL;
+	const gchar		*uri = NULL;
 	struct history		*h, find;
 	struct karg		a;
 	GdkColor		color;
@@ -3817,28 +3956,33 @@ notify_load_status_cb(WebKitWebView* wview, GParamSpec* pspec, struct tab *t)
 
 	case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
 		/* 3 */
+		if (color_visited_uris) {
+			color_visited(t, color_visited_helper());
+
+			/* This colors the links you middle-click (open in new
+			 * tab) in the current tab. */
+			if (t->tab_id != gtk_notebook_get_current_page(notebook) &&
+			    (uri = get_uri(t)) != NULL)
+				color_visited(get_current_tab(),
+				    g_strdup_printf("{'%s' : 'dummy'}", uri));
+		}
 		break;
 
 	case WEBKIT_LOAD_FINISHED:
 		/* 2 */
-		uri = get_uri(t);
-		if (uri == NULL)
+		if ((uri = get_uri(t)) == NULL)
 			return;
 
 		if (!strncmp(uri, "http://", strlen("http://")) ||
 		    !strncmp(uri, "https://", strlen("https://")) ||
 		    !strncmp(uri, "file://", strlen("file://"))) {
-			find.uri = uri;
+			find.uri = (gchar *)uri;
 			h = RB_FIND(history_list, &hl, &find);
-			if (!h) {
-				title = get_title(t, FALSE);
-				h = g_malloc(sizeof *h);
-				h->uri = g_strdup(uri);
-				h->title = g_strdup(title);
-				RB_INSERT(history_list, &hl, h);
-				completion_add_uri(h->uri);
-				update_history_tabs(NULL);
-			}
+			if (!h)
+				insert_history_item(uri,
+				    get_title(t, FALSE), time(NULL));
+			else
+				h->time = time(NULL);
 		}
 
 		set_status(t, (char *)uri, XT_STATUS_URI);
@@ -4858,13 +5002,13 @@ wv_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t)
 	/* don't use w directly; use t->whatever instead */
 
 	if (t == NULL) {
-		show_oops(NULL, "wv_keypress_after_cb");
+		show_oops(NULL, "wv_keypress_cb");
 		return (XT_CB_PASSTHROUGH);
 	}
 
 	hide_oops(t);
 
-	DNPRINTF(XT_D_KEY, "wv_keypress_after_cb: mode %d keyval 0x%x mask "
+	DNPRINTF(XT_D_KEY, "wv_keypress_cb: mode %d keyval 0x%x mask "
 	    "0x%x tab %d\n", t->mode, e->keyval, e->state, t->tab_id);
 
 	/* Hide buffers, if they are visible, with escape. */
@@ -4890,7 +5034,7 @@ wv_keypress_cb(GtkEntry *w, GdkEventKey *e, struct tab *t)
 		/* XXX make sure cmd entry is enabled */
 		return (XT_CB_HANDLED);
 	} else if (t->mode == XT_MODE_COMMAND) {
-		/* prefix input*/
+		/* prefix input */
 		snprintf(s, sizeof s, "%c", e->keyval);
 		if (CLEAN(e->state) == 0 && isdigit(s[0]))
 			cmd_prefix = 10 * cmd_prefix + atoi(s);
@@ -5923,7 +6067,7 @@ create_buffers(struct tab *t)
 	gtk_tree_view_set_model
 	    (GTK_TREE_VIEW(view), GTK_TREE_MODEL(buffers_store));
 
-	return view;
+	return (view);
 }
 
 void
diff --git a/xxxterm.conf b/xxxterm.conf
index 8fb1098..451ab89 100644
--- a/xxxterm.conf
+++ b/xxxterm.conf
@@ -20,6 +20,7 @@
 # enable_socket		= 0
 # enable_localstorage	= 0
 # single_instance	= 0
+# color_visited_uris	= 1
 # save_global_history	= 0
 # show_tabs		= 1
 # statusbar_elems	= BP
diff --git a/xxxterm.h b/xxxterm.h
index 49b281b..a721183 100644
--- a/xxxterm.h
+++ b/xxxterm.h
@@ -108,7 +108,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */
 
-/*#define XT_DEBUG*/
+#define XT_DEBUG
 #ifdef XT_DEBUG
 #define DPRINTF(x...)		do { if (swm_debug) fprintf(stderr, x); } while (0)
 #define DNPRINTF(n,x...)	do { if (swm_debug & n) fprintf(stderr, x); } while (0)
@@ -128,6 +128,8 @@ THE SOFTWARE.
 #define XT_D_CLIP		0x2000
 #define XT_D_BUFFERCMD		0x4000
 #define XT_D_INSPECTOR		0x8000
+#define XT_D_VISITED		0x10000
+#define XT_D_HISTORY		0x20000
 extern u_int32_t	swm_debug;
 #else
 #define DPRINTF(x...)
@@ -252,8 +254,9 @@ RB_PROTOTYPE(download_list, download, entry, download_rb_cmp);
 
 struct history {
 	RB_ENTRY(history)	entry;
-	const gchar		*uri;
-	const gchar		*title;
+	gchar			*uri;
+	gchar			*title;
+	time_t			time; /* When the item was added. */
 };
 RB_HEAD(history_list, history);
 RB_PROTOTYPE(history_list, history, entry, history_rb_cmp);
@@ -511,6 +514,7 @@ extern char	*http_proxy;
 extern char	download_dir[PATH_MAX];
 extern char	runtime_settings[PATH_MAX];
 extern int	allow_volatile_cookies;
+extern int	color_visited_uris;
 extern int	save_global_history;
 extern char	*user_agent;
 extern int	save_rejected_cookies;