about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--inputfocus.c196
-rw-r--r--xombrero.c36
-rw-r--r--xombrero.h7
3 files changed, 185 insertions, 54 deletions
diff --git a/inputfocus.c b/inputfocus.c
index 9115e66..fc6f259 100644
--- a/inputfocus.c
+++ b/inputfocus.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2011 Marco Peereboom <marco@peereboom.us>
+ * Copyright (c) 2012 Josh Rickmar <jrick@devio.us>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,6 +19,43 @@
 
 #if WEBKIT_CHECK_VERSION(1, 5, 0)
 	/* we got the DOM API we need */
+
+void
+focus_body(WebKitDOMDocument *doc)
+{
+	WebKitDOMNodeList       *body = NULL;
+	WebKitDOMNode           *n;
+	int                     i;       
+
+	body = webkit_dom_document_get_elements_by_tag_name(doc, "body");
+	for (i = 0; i < webkit_dom_node_list_get_length(body); ++i) {
+		n = webkit_dom_node_list_item(body, i);
+		webkit_dom_element_focus((WebKitDOMElement *)n);
+#if WEBKIT_CHECK_VERSION(1, 8, 0)
+		webkit_dom_html_element_click((WebKitDOMHTMLElement *)n);
+#endif
+		break;
+	}
+}
+
+int
+node_is_valid_entry(WebKitDOMNode *n)
+{
+	if (n == NULL)
+		return (FALSE);
+
+	if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(n) &&
+	    webkit_dom_html_input_element_check_validity(
+	    (WebKitDOMHTMLInputElement *)n))
+		return (TRUE);
+	if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(n) &&
+	    webkit_dom_html_text_area_element_check_validity(
+	    (WebKitDOMHTMLTextAreaElement *)n))
+		return (TRUE);
+
+	return (FALSE);
+}
+
 int
 focus_input_document(struct tab *t, WebKitDOMDocument *doc)
 {
@@ -37,13 +75,16 @@ focus_input_document(struct tab *t, WebKitDOMDocument *doc)
 		n = webkit_dom_node_list_item(input, i);
 		in = (WebKitDOMHTMLInputElement*)n;
 		g_object_get(G_OBJECT(in), "type", &es, (char *)NULL);
-		if ((!g_str_equal("text", es) && !g_str_equal("password",es)) ||
+		if ((g_strcmp0("text", es) && g_strcmp0("password",es)) ||
 		    webkit_dom_html_input_element_get_disabled(in)) {
 			/* skip not text */
 			g_free(es);
 			continue;
 		}
 		webkit_dom_element_focus((WebKitDOMElement*)in);
+#if WEBKIT_CHECK_VERSION(1, 8, 0)
+		webkit_dom_html_element_click((WebKitDOMHTMLElement*)in);
+#endif
 		g_free(es);
 		rv = 1; /* found */
 		goto done;
@@ -59,6 +100,9 @@ focus_input_document(struct tab *t, WebKitDOMDocument *doc)
 			continue;
 		}
 		webkit_dom_element_focus((WebKitDOMElement*)ta);
+#if WEBKIT_CHECK_VERSION(1, 8, 0)
+		webkit_dom_html_element_click((WebKitDOMHTMLElement*)ta);
+#endif
 		rv = 1; /* found */
 		goto done;
 	}
@@ -70,19 +114,39 @@ done:
 
 	return (rv);
 }
+
+char *
+get_element_text(WebKitDOMNode *n)
+{
+	if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(n))
+		return (g_strdup(webkit_dom_html_input_element_get_value(
+		    (WebKitDOMHTMLInputElement *)n)));
+	else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(n))
+		return (g_strdup(webkit_dom_html_text_area_element_get_value(
+		    (WebKitDOMHTMLTextAreaElement *)n)));
+	return (NULL);
+}
+
 int
 focus_input(struct tab *t)
 {
 	WebKitDOMDocument	*doc;
 	WebKitDOMNode		*n;
 	WebKitDOMNodeList	*fl = NULL, *ifl = NULL;
-	int			i, fl_count, ifl_count, rv = 0;
+	WebKitDOMElement	*a;
+	int			i, fl_count, ifl_count, rv = 0; /* not found */
 
 	WebKitDOMHTMLFrameElement	*frame;
 	WebKitDOMHTMLIFrameElement	*iframe;
 
 	/*
 	 * Here is what we are doing:
+	 *
+	 * If a textbox is already focused, leave it alone.
+	 *
+	 * Try the tab's previous active entry, for example if it was set by
+	 * some javascript when the page loaded.
+	 *
 	 * See if we got frames or iframes
 	 *
 	 * if we do focus on input or textarea in frame or in iframe
@@ -93,6 +157,30 @@ focus_input(struct tab *t)
 
 	doc = webkit_web_view_get_dom_document(t->wv);
 
+	/* try current active element */
+	a = webkit_dom_html_document_get_active_element(
+	    (WebKitDOMHTMLDocument*)doc);
+	if (node_is_valid_entry((WebKitDOMNode *)a)) {
+		rv = 1; /* found */
+		goto done;
+	}
+
+	/* try previous active element */
+	if (node_is_valid_entry((WebKitDOMNode *)t->active)) {
+		webkit_dom_element_focus((WebKitDOMElement*)t->active);
+#if WEBKIT_CHECK_VERSION(1, 8, 0)
+		webkit_dom_html_element_click((WebKitDOMHTMLElement*)t->active);
+#endif
+		rv = 1; /* found */
+		goto done;
+	} else {
+		t->active = NULL;
+		if (t->active_text) {
+			g_free(t->active_text);
+			t->active_text = NULL;
+		}
+	}
+
 	/* get frames */
 	fl = webkit_dom_document_get_elements_by_tag_name(doc, "frame");
 	fl_count = webkit_dom_node_list_get_length(fl);
@@ -108,7 +196,7 @@ focus_input(struct tab *t)
 		doc = webkit_dom_html_frame_element_get_content_document(frame);
 
 		if (focus_input_document(t, doc)) {
-			rv = 1;
+			rv = 1; /* focus */
 			goto done;
 		}
 	}
@@ -120,16 +208,15 @@ focus_input(struct tab *t)
 		doc = webkit_dom_html_iframe_element_get_content_document(iframe);
 
 		if (focus_input_document(t, doc)) {
-			rv = 1;
+			rv = 1; /* found */
 			goto done;
 		}
 	}
 
 	/* if we made it here nothing got focused so use normal heuristic */
-	if (focus_input_document(t, webkit_web_view_get_dom_document(t->wv))) {
-		rv = 1;
-		goto done;
-	}
+	if (focus_input_document(t, webkit_web_view_get_dom_document(t->wv)))
+		rv = 1; /* found */
+
 done:
 	if (fl)
 		g_object_unref(fl);
@@ -140,7 +227,7 @@ done:
 }
 
 int
-dom_is_input(struct tab *t, WebKitDOMElement **active)
+dom_is_input(struct tab *t, char **text)
 {
 	WebKitDOMDocument	*doc;
 	WebKitDOMElement	*a;
@@ -193,7 +280,11 @@ dom_is_input(struct tab *t, WebKitDOMElement **active)
 		aa = (WebKitDOMHTMLElement*)a;
 		if (WEBKIT_DOM_IS_HTML_ELEMENT(aa) &&
 		    webkit_dom_html_element_get_is_content_editable(aa)) {
-			*active = a;
+			if (t->active == NULL)
+				t->active = a;
+			*text = get_element_text((WebKitDOMNode *)a);
+			if (t->active_text == NULL)
+				t->active_text = g_strdup(*text);
 			return (1);
 		}
 		break;
@@ -202,9 +293,19 @@ dom_is_input(struct tab *t, WebKitDOMElement **active)
 	if (a == NULL)
 		return (0);
 
-	if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT((WebKitDOMNode *)a) ||
-	    WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT((WebKitDOMNode *)a)) {
-		*active = a;
+	if (node_is_valid_entry((WebKitDOMNode *)a)) {
+		if (!node_is_valid_entry((WebKitDOMNode *)t->active)) {
+			t->active = NULL;
+			if (t->active_text) {
+				g_free(t->active_text);
+				t->active_text = NULL;
+			}
+		}
+		if (t->active == NULL)
+			t->active = a;
+		*text = get_element_text((WebKitDOMNode *)a);
+		if (t->active_text == NULL)
+			t->active_text = g_strdup(*text);
 		return (1);
 	}
 
@@ -214,35 +315,41 @@ dom_is_input(struct tab *t, WebKitDOMElement **active)
 void *
 input_check_mode(struct tab *t)
 {
-	WebKitDOMElement	*active = NULL;
+	char			*text = NULL;
 
-	if (dom_is_input(t, &active))
+	if (dom_is_input(t, &text)) {
 		t->mode = XT_MODE_INSERT;
-
-	return (active);
-}
-
-void
-input_focus_blur(struct tab *t, void *active)
-{
-	/* active is (WebKitDOMElement*) */
-	if (active)
-		webkit_dom_element_blur(active);
+		return (t->active);
+	} else
+		return (NULL);
 }
 
 int
 command_mode(struct tab *t, struct karg *args)
 {
-	WebKitDOMElement	*active = NULL;
+	WebKitDOMDocument	*doc;
+	WebKitDOMElement	*a;
 
 	if (args->i == XT_MODE_COMMAND) {
-		if (dom_is_input(t, &active))
-			if (active)
-				webkit_dom_element_blur(active);
+		doc = webkit_web_view_get_dom_document(t->wv);
+		a = webkit_dom_html_document_get_active_element(
+		    (WebKitDOMHTMLDocument *)doc);
+		if (a) {
+			webkit_dom_element_blur(a);
+			focus_body(doc);
+		}
 		t->mode = XT_MODE_COMMAND;
-	} else {
-		if (focus_input(t))
-			t->mode = XT_MODE_INSERT;
+	} else if (args->i == XT_MODE_INSERT && focus_input(t))
+		t->mode = XT_MODE_INSERT;
+	else if (args->i == XT_MODE_HINT || args->i == XT_MODE_PASSTHROUGH)
+		t->mode = args->i;
+
+	if (!node_is_valid_entry((WebKitDOMNode *)t->active)) {
+		t->active = NULL;
+		if (t->active_text) {
+			g_free(t->active_text);
+			t->active_text = NULL;
+		}
 	}
 
 	return (XT_CB_HANDLED);
@@ -251,7 +358,8 @@ command_mode(struct tab *t, struct karg *args)
 void
 input_autofocus(struct tab *t)
 {
-	WebKitDOMElement	*active = NULL;
+	struct karg		args = {0};
+	char			*text = NULL;
 
 	if (autofocus_onload &&
 	    t->tab_id == gtk_notebook_get_current_page(notebook)) {
@@ -260,11 +368,18 @@ input_autofocus(struct tab *t)
 		else
 			t->mode = XT_MODE_COMMAND;
 	} else {
-		if (dom_is_input(t, &active))
-			if (active)
-				webkit_dom_element_blur(active);
-		t->mode = XT_MODE_COMMAND;
+		if (dom_is_input(t, &text)) {
+			if (text != NULL && g_strcmp0(text, t->active_text))
+				args.i = XT_MODE_INSERT;
+			else
+				args.i = XT_MODE_COMMAND;
+		} else
+			args.i = XT_MODE_COMMAND;
+		command_mode(t, &args);
 	}
+
+	if (text)
+		g_free(text);
 }
 #else /* WEBKIT_CHECK_VERSION */
 	/* incomplete DOM API */
@@ -288,13 +403,6 @@ input_autofocus(struct tab *t)
 	}
 }
 
-void
-input_focus_blur(struct tab *t, void *active)
-{
-	run_script(t, "hints.clearFocus();");
-	t->mode = XT_MODE_COMMAND;
-}
-
 void *
 input_check_mode(struct tab *t)
 {
diff --git a/xombrero.c b/xombrero.c
index d02d471..53b68f3 100644
--- a/xombrero.c
+++ b/xombrero.c
@@ -476,6 +476,7 @@ hide_cmd(struct tab *t)
 
 	history_at = NULL; /* just in case */
 	search_at = NULL; /* just in case */
+	gtk_widget_set_can_focus(t->cmd, FALSE);
 	gtk_widget_hide(t->cmd);
 }
 
@@ -487,6 +488,7 @@ show_cmd(struct tab *t)
 	history_at = NULL;
 	search_at = NULL;
 	gtk_widget_hide(t->oops);
+	gtk_widget_set_can_focus(t->cmd, TRUE);
 	gtk_widget_show(t->cmd);
 }
 
@@ -494,6 +496,7 @@ void
 hide_buffers(struct tab *t)
 {
 	gtk_widget_hide(t->buffers);
+	gtk_widget_set_can_focus(t->buffers, FALSE);
 	gtk_list_store_clear(buffers_store);
 }
 
@@ -567,6 +570,7 @@ show_buffers(struct tab *t)
 	gtk_tree_path_free(path);
 
 	gtk_widget_show(t->buffers);
+	gtk_widget_set_can_focus(t->buffers, TRUE);
 	gtk_widget_grab_focus(GTK_WIDGET(t->buffers));
 }
 
@@ -2805,7 +2809,7 @@ struct prompt_sub {
 int
 command(struct tab *t, struct karg *args)
 {
-	struct karg		a;
+	struct karg		a = {0};
 	int			i;
 	char			*s = NULL, *sp = NULL, *sl = NULL;
 	gchar			**sv;
@@ -2851,14 +2855,12 @@ command(struct tab *t, struct karg *args)
 		break;
 	case '.':
 		t->mode = XT_MODE_HINT;
-		bzero(&a, sizeof a);
 		a.i = 0;
 		hint(t, &a);
 		s = ".";
 		break;
 	case ',':
 		t->mode = XT_MODE_HINT;
-		bzero(&a, sizeof a);
 		a.i = XT_HINT_NEWTAB;
 		hint(t, &a);
 		s = ",";
@@ -4227,6 +4229,15 @@ notify_load_status_cb(WebKitWebView* wview, GParamSpec* pspec, struct tab *t)
 		g_free(base);
 #endif
 
+		/* DOM is changing, unreference the previous focused element */
+#if WEBKIT_CHECK_VERSION(1, 5, 0)
+		t->active = NULL;
+		if (t->active_text) {
+			g_free(t->active_text);
+			t->active_text = NULL;
+		}
+#endif
+
 		/* take focus if we are visible */
 		focus_webview(t);
 		t->focus_wv = 1;
@@ -7000,6 +7011,7 @@ create_browser(struct tab *t)
 	}
 
 	w = gtk_scrolled_window_new(NULL, NULL);
+	gtk_widget_set_can_focus(w, FALSE);
 	t->adjust_h = gtk_scrolled_window_get_hadjustment(
 	    GTK_SCROLLED_WINDOW(w));
 	t->adjust_v = gtk_scrolled_window_get_vadjustment(
@@ -7115,16 +7127,13 @@ create_toolbar(struct tab *t)
 	gtk_widget_set_sensitive(t->forward, FALSE);
 	g_signal_connect(G_OBJECT(t->forward), "clicked",
 	    G_CALLBACK(forward_cb), t);
-	gtk_box_pack_start(GTK_BOX(b), t->forward, FALSE,
-	    FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(b), t->forward, FALSE, FALSE, 0);
 
 	/* stop button */
 	t->stop = create_button("Stop", GTK_STOCK_STOP, 0);
 	gtk_widget_set_sensitive(t->stop, FALSE);
-	g_signal_connect(G_OBJECT(t->stop), "clicked",
-	    G_CALLBACK(stop_cb), t);
-	gtk_box_pack_start(GTK_BOX(b), t->stop, FALSE,
-	    FALSE, 0);
+	g_signal_connect(G_OBJECT(t->stop), "clicked", G_CALLBACK(stop_cb), t);
+	gtk_box_pack_start(GTK_BOX(b), t->stop, FALSE, FALSE, 0);
 
 	/* JS button */
 	t->js_toggle = create_button("JS-Toggle", enable_scripts ?
@@ -7651,13 +7660,19 @@ create_new_tab(char *title, struct undo *u, int focus, int position)
 	t->vbox = gtk_vbox_new(FALSE, 0);
 	b = gtk_hbox_new(FALSE, 0);
 #endif
+	gtk_widget_set_can_focus(t->vbox, FALSE);
 
 	/* label + button for tab */
 	t->tab_content = b;
+	gtk_widget_set_can_focus(t->tab_content, FALSE);
 
 	t->user_agent_id = 0;
 	t->http_accept_id = 0;
 
+#if WEBKIT_CHECK_VERSION(1, 5, 0)
+	t->active = NULL;
+#endif
+
 #if GTK_CHECK_VERSION(2, 20, 0)
 	t->spinner = gtk_spinner_new();
 #endif
@@ -8068,6 +8083,7 @@ create_button(char *name, char *stockid, int size)
 	g_free(newstyle);
 #endif
 	button = gtk_button_new();
+	gtk_widget_set_can_focus(button, FALSE);
 	gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
 	gtk_icon_size = icon_size_map(size ? size : icon_size);
 
@@ -8107,6 +8123,7 @@ create_canvas(void)
 	vbox = gtk_vbox_new(FALSE, 0);
 #endif
 	gtk_box_set_spacing(GTK_BOX(vbox), 0);
+	gtk_widget_set_can_focus(vbox, FALSE);
 	notebook = GTK_NOTEBOOK(gtk_notebook_new());
 #if !GTK_CHECK_VERSION(3, 0, 0)
 	/* XXX seems to be needed with gtk+2 */
@@ -8117,6 +8134,7 @@ create_canvas(void)
 	gtk_widget_set_can_focus(GTK_WIDGET(notebook), FALSE);
 
 	abtn = gtk_button_new();
+	gtk_widget_set_can_focus(abtn, FALSE);
 	arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
 	gtk_widget_set_name(abtn, "Arrow");
 	gtk_container_add(GTK_CONTAINER(abtn), arrow);
diff --git a/xombrero.h b/xombrero.h
index 64ac54a..094e3cc 100644
--- a/xombrero.h
+++ b/xombrero.h
@@ -285,6 +285,12 @@ struct tab {
 	WebKitWebInspector	*inspector;
 	GtkWidget		*inspector_window;
 	GtkWidget		*inspector_view;
+
+	/* focused text entry */
+#if WEBKIT_CHECK_VERSION(1, 5, 0)
+	WebKitDOMElement	*active;
+	char			*active_text;
+#endif
 };
 TAILQ_HEAD(tab_list, tab);
 
@@ -566,7 +572,6 @@ int		toggle_force_https(struct tab *, struct karg *);
 
 /* input autofocus */
 void		input_autofocus(struct tab *);
-void		input_focus_blur(struct tab *, void *);
 void		*input_check_mode(struct tab *);
 int		command_mode(struct tab *, struct karg *);