2 * Copyright (c) 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2012, 2013 Josh Rickmar <jrick@devio.us>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #if WEBKIT_CHECK_VERSION(1, 5, 0)
21 /* we got the DOM API we need */
24 focus_body(WebKitDOMDocument
*doc
)
26 WebKitDOMNodeList
*body
= NULL
;
30 body
= webkit_dom_document_get_elements_by_tag_name(doc
, "body");
31 for (i
= 0; i
< webkit_dom_node_list_get_length(body
); ++i
) {
32 n
= webkit_dom_node_list_item(body
, i
);
33 webkit_dom_element_focus((WebKitDOMElement
*)n
);
34 #if WEBKIT_CHECK_VERSION(1, 8, 0)
35 webkit_dom_html_element_click((WebKitDOMHTMLElement
*)n
);
42 node_is_valid_entry(WebKitDOMNode
*n
)
47 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(n
) &&
48 webkit_dom_html_input_element_check_validity(
49 (WebKitDOMHTMLInputElement
*)n
))
51 if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(n
) &&
52 webkit_dom_html_text_area_element_check_validity(
53 (WebKitDOMHTMLTextAreaElement
*)n
))
60 focus_input_document(struct tab
*t
, WebKitDOMDocument
*doc
)
62 WebKitDOMNodeList
*input
= NULL
, *textarea
= NULL
;
65 int i
, rv
= 0 /* not found */;
67 WebKitDOMHTMLTextAreaElement
*ta
;
68 WebKitDOMHTMLInputElement
*in
;
70 /* we are deliberately ignoring tab index! */
73 input
= webkit_dom_document_get_elements_by_tag_name(doc
, "input");
74 for (i
= 0; i
< webkit_dom_node_list_get_length(input
); i
++) {
75 n
= webkit_dom_node_list_item(input
, i
);
76 in
= (WebKitDOMHTMLInputElement
*)n
;
77 g_object_get(G_OBJECT(in
), "type", &es
, (char *)NULL
);
78 if ((g_strcmp0("text", es
) && g_strcmp0("password",es
)) ||
79 webkit_dom_html_input_element_get_disabled(in
)) {
84 webkit_dom_element_focus((WebKitDOMElement
*)in
);
85 #if WEBKIT_CHECK_VERSION(1, 8, 0)
86 webkit_dom_html_element_click((WebKitDOMHTMLElement
*)in
);
93 /* now try textarea */
94 textarea
= webkit_dom_document_get_elements_by_tag_name(doc
, "textarea");
95 for (i
= 0; i
< webkit_dom_node_list_get_length(textarea
); i
++) {
96 n
= webkit_dom_node_list_item(textarea
, i
);
97 ta
= (WebKitDOMHTMLTextAreaElement
*)n
;
98 if (webkit_dom_html_text_area_element_get_disabled(ta
)) {
99 /* it is hidden so skip */
102 webkit_dom_element_focus((WebKitDOMElement
*)ta
);
103 #if WEBKIT_CHECK_VERSION(1, 8, 0)
104 webkit_dom_html_element_click((WebKitDOMHTMLElement
*)ta
);
111 g_object_unref(input
);
113 g_object_unref(textarea
);
119 get_element_text(WebKitDOMNode
*n
)
121 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(n
))
122 return (g_strdup(webkit_dom_html_input_element_get_value(
123 (WebKitDOMHTMLInputElement
*)n
)));
124 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(n
))
125 return (g_strdup(webkit_dom_html_text_area_element_get_value(
126 (WebKitDOMHTMLTextAreaElement
*)n
)));
131 focus_input(struct tab
*t
)
133 WebKitDOMDocument
*doc
;
135 WebKitDOMNodeList
*fl
= NULL
, *ifl
= NULL
;
137 int i
, fl_count
, ifl_count
, rv
= 0; /* not found */
139 WebKitDOMHTMLFrameElement
*frame
;
140 WebKitDOMHTMLIFrameElement
*iframe
;
143 * Here is what we are doing:
145 * If a textbox is already focused, leave it alone.
147 * Try the tab's previous active entry, for example if it was set by
148 * some javascript when the page loaded.
150 * See if we got frames or iframes
152 * if we do focus on input or textarea in frame or in iframe
154 * if we find nothing or there are no frames focus on first input or
158 doc
= webkit_web_view_get_dom_document(t
->wv
);
159 #if WEBKIT_CHECK_VERSION(2, 0, 0)
160 /* This check is broken on old webkit */
161 if (!WEBKIT_DOM_IS_HTML_DOCUMENT(doc
)) {
162 show_oops(t
, "%s: DOM node is not a valid HTML document",
168 /* try current active element */
169 a
= webkit_dom_html_document_get_active_element(
170 (WebKitDOMHTMLDocument
*)doc
);
171 if (node_is_valid_entry((WebKitDOMNode
*)a
)) {
176 /* try previous active element */
177 if (node_is_valid_entry((WebKitDOMNode
*)t
->active
)) {
178 webkit_dom_element_focus((WebKitDOMElement
*)t
->active
);
179 #if WEBKIT_CHECK_VERSION(1, 8, 0)
180 webkit_dom_html_element_click((WebKitDOMHTMLElement
*)t
->active
);
186 g_object_unref(t
->active
);
188 if (t
->active_text
) {
189 g_free(t
->active_text
);
190 t
->active_text
= NULL
;
195 fl
= webkit_dom_document_get_elements_by_tag_name(doc
, "frame");
196 fl_count
= webkit_dom_node_list_get_length(fl
);
199 ifl
= webkit_dom_document_get_elements_by_tag_name(doc
, "iframe");
200 ifl_count
= webkit_dom_node_list_get_length(ifl
);
202 /* walk frames and look for a text input */
203 for (i
= 0; i
< fl_count
; i
++) {
204 n
= webkit_dom_node_list_item(fl
, i
);
205 frame
= (WebKitDOMHTMLFrameElement
*)n
;
206 doc
= webkit_dom_html_frame_element_get_content_document(frame
);
208 if (focus_input_document(t
, doc
)) {
214 /* walk iframes and look for a text input */
215 for (i
= 0; i
< ifl_count
; i
++) {
216 n
= webkit_dom_node_list_item(ifl
, i
);
217 iframe
= (WebKitDOMHTMLIFrameElement
*)n
;
218 doc
= webkit_dom_html_iframe_element_get_content_document(iframe
);
220 if (focus_input_document(t
, doc
)) {
226 /* if we made it here nothing got focused so use normal heuristic */
227 if (focus_input_document(t
, webkit_web_view_get_dom_document(t
->wv
)))
240 dom_is_input(struct tab
*t
, char **text
)
242 WebKitDOMDocument
*doc
;
244 WebKitDOMHTMLElement
*aa
;
245 WebKitDOMHTMLObjectElement
*object
;
247 WebKitDOMHTMLFrameElement
*frame
;
248 WebKitDOMHTMLIFrameElement
*iframe
;
250 /* proof positive that OO is stupid */
252 doc
= webkit_web_view_get_dom_document(t
->wv
);
254 /* unwind frames and iframes until the cows come home */
256 #if WEBKIT_CHECK_VERSION(2, 0, 0)
257 if (!WEBKIT_DOM_IS_HTML_DOCUMENT(doc
))
260 a
= webkit_dom_html_document_get_active_element(
261 (WebKitDOMHTMLDocument
*)doc
);
265 frame
= (WebKitDOMHTMLFrameElement
*)a
;
266 if (WEBKIT_DOM_IS_HTML_FRAME_ELEMENT(frame
)) {
267 doc
= webkit_dom_html_frame_element_get_content_document(
272 iframe
= (WebKitDOMHTMLIFrameElement
*)a
;
273 if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(iframe
)) {
274 doc
= webkit_dom_html_iframe_element_get_content_document(
279 object
= (WebKitDOMHTMLObjectElement
*)a
;
280 if (WEBKIT_DOM_IS_HTML_OBJECT_ELEMENT(object
)) {
281 doc
= webkit_dom_html_object_element_get_content_document(
287 * I think this is a total hack because this property isn't
288 * set for textareas or input however, it is set for jquery
289 * textareas that do rich text. Since this works around issues
290 * in RT we'll simply keep it!
292 * This might break some other stuff but for now it helps.
294 aa
= (WebKitDOMHTMLElement
*)a
;
295 if (WEBKIT_DOM_IS_HTML_ELEMENT(aa
) &&
296 webkit_dom_html_element_get_is_content_editable(aa
)) {
297 if (t
->active
== NULL
) {
299 g_object_ref(t
->active
);
301 *text
= get_element_text((WebKitDOMNode
*)a
);
302 if (t
->active_text
== NULL
)
303 t
->active_text
= g_strdup(*text
);
312 if (node_is_valid_entry((WebKitDOMNode
*)a
)) {
313 if (!node_is_valid_entry((WebKitDOMNode
*)t
->active
)) {
315 g_object_unref(t
->active
);
317 if (t
->active_text
) {
318 g_free(t
->active_text
);
319 t
->active_text
= NULL
;
322 if (t
->active
== NULL
) {
324 g_object_ref(t
->active
);
326 *text
= get_element_text((WebKitDOMNode
*)a
);
327 if (t
->active_text
== NULL
)
328 t
->active_text
= g_strdup(*text
);
336 input_check_mode(struct tab
*t
)
340 if (dom_is_input(t
, &text
)) {
341 t
->mode
= XT_MODE_INSERT
;
348 command_mode(struct tab
*t
, struct karg
*args
)
350 WebKitDOMDocument
*doc
;
353 if (args
->i
== XT_MODE_COMMAND
) {
354 doc
= webkit_web_view_get_dom_document(t
->wv
);
355 #if WEBKIT_CHECK_VERSION(2, 0, 0)
356 if (!WEBKIT_DOM_IS_HTML_DOCUMENT(doc
)) {
357 show_oops(t
, "%s: DOM node is not a valid HTML "
358 "document", __func__
);
359 return (XT_CB_HANDLED
);
362 a
= webkit_dom_html_document_get_active_element(
363 (WebKitDOMHTMLDocument
*)doc
);
365 webkit_dom_element_blur(a
);
368 t
->mode
= XT_MODE_COMMAND
;
369 } else if (args
->i
== XT_MODE_INSERT
&& focus_input(t
))
370 t
->mode
= XT_MODE_INSERT
;
371 else if (args
->i
== XT_MODE_HINT
|| args
->i
== XT_MODE_PASSTHROUGH
)
374 if (!node_is_valid_entry((WebKitDOMNode
*)t
->active
)) {
376 g_object_unref(t
->active
);
378 if (t
->active_text
) {
379 g_free(t
->active_text
);
380 t
->active_text
= NULL
;
384 return (XT_CB_HANDLED
);
388 input_autofocus(struct tab
*t
)
390 struct karg args
= {0};
393 if (autofocus_onload
&&
394 t
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
396 t
->mode
= XT_MODE_INSERT
;
398 t
->mode
= XT_MODE_COMMAND
;
400 if (dom_is_input(t
, &text
)) {
401 if (text
!= NULL
&& g_strcmp0(text
, t
->active_text
))
402 args
.i
= XT_MODE_INSERT
;
404 args
.i
= XT_MODE_COMMAND
;
406 args
.i
= XT_MODE_COMMAND
;
407 command_mode(t
, &args
);
413 #else /* WEBKIT_CHECK_VERSION */
414 /* incomplete DOM API */
418 * note that we can't check the return value of run_script so we
419 * have to assume that the command worked; this may leave you in
420 * insertmode when in fact you shouldn't be
423 input_autofocus(struct tab
*t
)
425 if (autofocus_onload
&&
426 t
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
427 run_script(t
, "hints.focusInput();");
428 t
->mode
= XT_MODE_INSERT
;
430 run_script(t
, "hints.clearFocus();");
431 t
->mode
= XT_MODE_COMMAND
;
436 input_check_mode(struct tab
*t
)
442 command_mode(struct tab
*t
, struct karg
*args
)
444 if (args
->i
== XT_MODE_COMMAND
) {
445 run_script(t
, "hints.clearFocus();");
446 t
->mode
= XT_MODE_COMMAND
;
448 run_script(t
, "hints.focusInput();");
449 t
->mode
= XT_MODE_INSERT
;
452 return (XT_CB_HANDLED
);