1 /**
2 * vimb - a webkit based vim like browser.
3 *
4 * Copyright (C) 2012-2018 Daniel Carl
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://www.gnu.org/licenses/.
18 */
19
20 #include <glib.h>
21 #include <webkitdom/webkitdom.h>
22
23 #include "ext-main.h"
24 #include "ext-dom.h"
25
26 static gboolean is_element_visible(WebKitDOMHTMLElement *element);
27
28
29 /**
30 * Checks if given dom element is an editable element.
31 */
ext_dom_is_editable(WebKitDOMElement * element)32 gboolean ext_dom_is_editable(WebKitDOMElement *element)
33 {
34 char *type;
35 gboolean result = FALSE;
36
37 if (!element) {
38 return FALSE;
39 }
40
41 /* element is editable if it's a text area or input with no type, text or
42 * password */
43 if (webkit_dom_html_element_get_is_content_editable(WEBKIT_DOM_HTML_ELEMENT(element))
44 || WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(element)) {
45 return TRUE;
46 }
47
48 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(element)) {
49 type = webkit_dom_html_input_element_get_input_type(WEBKIT_DOM_HTML_INPUT_ELEMENT(element));
50 /* Input element without type attribute are rendered and behave like
51 * type = text and there are a lot of pages in the wild using input
52 * field without type attribute. */
53 if (!*type
54 || !g_ascii_strcasecmp(type, "text")
55 || !g_ascii_strcasecmp(type, "password")
56 || !g_ascii_strcasecmp(type, "color")
57 || !g_ascii_strcasecmp(type, "date")
58 || !g_ascii_strcasecmp(type, "datetime")
59 || !g_ascii_strcasecmp(type, "datetime-local")
60 || !g_ascii_strcasecmp(type, "email")
61 || !g_ascii_strcasecmp(type, "month")
62 || !g_ascii_strcasecmp(type, "number")
63 || !g_ascii_strcasecmp(type, "search")
64 || !g_ascii_strcasecmp(type, "tel")
65 || !g_ascii_strcasecmp(type, "time")
66 || !g_ascii_strcasecmp(type, "url")
67 || !g_ascii_strcasecmp(type, "week"))
68 {
69 result = TRUE;
70 }
71
72 g_free(type);
73 }
74
75 return result;
76 }
77
78 /**
79 * Find the first editable element and set the focus on it and enter input
80 * mode.
81 * Returns true if there was an editable element focused.
82 */
ext_dom_focus_input(WebKitDOMDocument * doc)83 gboolean ext_dom_focus_input(WebKitDOMDocument *doc)
84 {
85 WebKitDOMNode *html, *node;
86 WebKitDOMHTMLCollection *collection;
87 WebKitDOMXPathNSResolver *resolver;
88 WebKitDOMXPathResult* result;
89 WebKitDOMDocument *frame_doc;
90 guint i, len;
91
92 collection = webkit_dom_document_get_elements_by_tag_name_as_html_collection(doc, "html");
93 if (!collection) {
94 return FALSE;
95 }
96
97 html = webkit_dom_html_collection_item(collection, 0);
98 g_object_unref(collection);
99
100 resolver = webkit_dom_document_create_ns_resolver(doc, html);
101 if (!resolver) {
102 return FALSE;
103 }
104
105 /* Use translate to match xpath expression case insensitive so that also
106 * intput filed of type="TEXT" are matched. */
107 result = webkit_dom_document_evaluate(
108 doc, "//input[not(@type) "
109 "or translate(@type,'ETX','etx')='text' "
110 "or translate(@type,'ADOPRSW','adoprsw')='password' "
111 "or translate(@type,'CLOR','clor')='color' "
112 "or translate(@type,'ADET','adet')='date' "
113 "or translate(@type,'ADEIMT','adeimt')='datetime' "
114 "or translate(@type,'ACDEILMOT','acdeilmot')='datetime-local' "
115 "or translate(@type,'AEILM','aeilm')='email' "
116 "or translate(@type,'HMNOT','hmnot')='month' "
117 "or translate(@type,'BEMNRU','bemnru')='number' "
118 "or translate(@type,'ACEHRS','acehrs')='search' "
119 "or translate(@type,'ELT','elt')='tel' "
120 "or translate(@type,'EIMT','eimt')='time' "
121 "or translate(@type,'LRU','lru')='url' "
122 "or translate(@type,'EKW','ekw')='week' "
123 "]|//textarea",
124 html, resolver, 5, NULL, NULL
125 );
126 if (!result) {
127 return FALSE;
128 }
129 while ((node = webkit_dom_xpath_result_iterate_next(result, NULL))) {
130 if (is_element_visible(WEBKIT_DOM_HTML_ELEMENT(node))) {
131 webkit_dom_element_focus(WEBKIT_DOM_ELEMENT(node));
132 return TRUE;
133 }
134 }
135
136 /* Look for editable elements in frames too. */
137 collection = webkit_dom_document_get_elements_by_tag_name_as_html_collection(doc, "iframe");
138 len = webkit_dom_html_collection_get_length(collection);
139
140 for (i = 0; i < len; i++) {
141 node = webkit_dom_html_collection_item(collection, i);
142 frame_doc = webkit_dom_html_iframe_element_get_content_document(WEBKIT_DOM_HTML_IFRAME_ELEMENT(node));
143 /* Stop on first frame with focused element. */
144 if (ext_dom_focus_input(frame_doc)) {
145 g_object_unref(collection);
146 return TRUE;
147 }
148 }
149 g_object_unref(collection);
150
151 return FALSE;
152 }
153
154 /**
155 * Retrieves the content of given editable element.
156 * Not that the returned value must be freed.
157 */
ext_dom_editable_get_value(WebKitDOMElement * element)158 char *ext_dom_editable_get_value(WebKitDOMElement *element)
159 {
160 char *value = NULL;
161
162 if ((webkit_dom_html_element_get_is_content_editable(WEBKIT_DOM_HTML_ELEMENT(element)))) {
163 value = webkit_dom_html_element_get_inner_text(WEBKIT_DOM_HTML_ELEMENT(element));
164 } else if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(WEBKIT_DOM_HTML_INPUT_ELEMENT(element))) {
165 value = webkit_dom_html_input_element_get_value(WEBKIT_DOM_HTML_INPUT_ELEMENT(element));
166 } else {
167 value = webkit_dom_html_text_area_element_get_value(WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT(element));
168 }
169
170 return value;
171 }
172
ext_dom_lock_input(WebKitDOMDocument * parent,char * element_id)173 void ext_dom_lock_input(WebKitDOMDocument *parent, char *element_id)
174 {
175 WebKitDOMElement *elem;
176
177 elem = webkit_dom_document_get_element_by_id(parent, element_id);
178 if (elem != NULL) {
179 webkit_dom_element_set_attribute(elem, "disabled", "true", NULL);
180 }
181 }
182
ext_dom_unlock_input(WebKitDOMDocument * parent,char * element_id)183 void ext_dom_unlock_input(WebKitDOMDocument *parent, char *element_id)
184 {
185 WebKitDOMElement *elem;
186
187 elem = webkit_dom_document_get_element_by_id(parent, element_id);
188 if (elem != NULL) {
189 webkit_dom_element_remove_attribute(elem, "disabled");
190 webkit_dom_element_focus(elem);
191 }
192 }
193
194 /**
195 * Indicates if the give nelement is visible.
196 */
is_element_visible(WebKitDOMHTMLElement * element)197 static gboolean is_element_visible(WebKitDOMHTMLElement *element)
198 {
199 return TRUE;
200 }
201
202