Merge lp:~midori/midori/webkitdom into lp:midori

Proposed by Christian Dywan
Status: Work in progress
Proposed branch: lp:~midori/midori/webkitdom
Merge into: lp:midori
Diff against target: 312 lines (+208/-23)
4 files modified
midori/midori-tab.vala (+182/-1)
midori/midori-view.c (+0/-22)
midori/webkitgtk-3.0.vapi (+1/-0)
tests/tab.vala (+25/-0)
To merge this branch: bzr merge lp:~midori/midori/webkitdom
Reviewer Review Type Date Requested Status
Midori Devs Pending
Review via email: mp+174887@code.launchpad.net
To post a comment you must log in.

Unmerged revisions

6171. By Christian Dywan

Port link hints to DOM code in Midori.Tab

6170. By Christian Dywan

Rework inline find with custom CSS styling

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'midori/midori-tab.vala'
2--- midori/midori-tab.vala 2013-06-21 23:23:12 +0000
3+++ midori/midori-tab.vala 2013-07-15 22:57:23 +0000
4@@ -35,6 +35,11 @@
5
6 public class Tab : Gtk.VBox {
7 public WebKit.WebView web_view { get; private set; }
8+ int find_links;
9+ private string css_class_match;
10+ private string css_class_found;
11+ private string css_class_hint;
12+ private string css_class_key;
13
14 #if HAVE_GRANITE_CLUTTER
15 public Granite.Widgets.NavigationBox navigation_box { get; private set; }
16@@ -105,6 +110,11 @@
17 web_view = new WebKit.WebView ();
18 /* Load something to avoid a bug where WebKit might not set a main frame */
19 web_view.load_uri ("");
20+ css_class_match = "midori" + Checksum.compute_for_string (ChecksumType.MD5, "search-match");
21+ css_class_found = "midori" + Checksum.compute_for_string (ChecksumType.MD5, "search-found");
22+ css_class_hint = "midori" + Checksum.compute_for_string (ChecksumType.MD5, "search-hint");
23+ css_class_key = "midori" + Checksum.compute_for_string (ChecksumType.MD5, "search-key");
24+ setup_styles ();
25 }
26
27 public void inject_stylesheet (string stylesheet) {
28@@ -222,9 +232,115 @@
29
30 public void unmark_text_matches () {
31 #if !HAVE_WEBKIT2
32+#if HAVE_DOM
33+ var dom = web_view.get_dom_document ();
34+ var nodes = dom.get_elements_by_class_name (css_class_match);
35+ for (ulong i = 0; i < nodes.length; i++) {
36+ var node = nodes.item (i);
37+ var parent = node.parent_node;
38+ string inner_html;
39+ parent.get ("inner-html", out inner_html);
40+ var text_node = dom.create_text_node (node.text_content);
41+ parent.replace_child (text_node, node);
42+ parent.normalize ();
43+ string after;
44+ parent.get ("inner-html", out after);
45+ stdout.printf ("unmark before: %s\nafter: %s\n", inner_html, after);
46+ }
47+
48+ /* FIXME There shouldn't be any remnants left */
49+ nodes = dom.get_elements_by_class_name (css_class_match);
50+ if (nodes.length != 0) {
51+ for (ulong i = 0; i < nodes.length; i++) {
52+ var node = nodes.item (i);
53+ string remnant;
54+ node.get ("inner-html", out remnant);
55+ stdout.printf ("! %s\n", remnant);
56+ node.parent_node.remove_child (node);
57+ }
58+ }
59+#else
60 web_view.unmark_text_matches ();
61 #endif
62- }
63+#endif
64+ }
65+
66+#if !HAVE_WEBKIT2
67+#if HAVE_DOM
68+ void setup_styles () {
69+ string css_style = """
70+ .%s, .%s {
71+ padding: 0.1em !important;
72+ border: 1px solid transparent !important;
73+ border-radius: 0.3em !important;
74+ z-index: 555 !important;
75+ position: absolute !important;
76+ display: inline !important;
77+ color: #000 !important;
78+ background-color: #FCE94F !important;
79+ border-top-color: ##E8D539 !important;
80+ border-left-color: ##E8D539 !important;
81+ border-bottom-color: #F5E988 !important;
82+ border-right-color: #F5E988 !important;
83+ }
84+ .%s {
85+ background-color: #d1eeb9 !important;
86+ border-top-color: #C8D6BC !important;
87+ border-left-color: #C8D6BC !important;
88+ border-bottom-color: #93C967 !important;
89+ border-right-color: #93C967 !important;
90+ }
91+ """.printf (css_class_match, css_class_found, css_class_match);
92+ /* FIXME: style ::selection OR Source/WebCore/editing/Editor.cpp findString */
93+ inject_stylesheet (css_style);
94+ }
95+
96+ public override virtual signal bool navigation_policy_decision_requested (
97+ WebKit.WebFrame frame, WebKit.NetworkRequest request,
98+ WebKit.WebNavigationAction action, WebKit.WebPolicyDecision decision) {
99+
100+ setup_styles ();
101+ return false;
102+ }
103+
104+ void handle_link_hints (Gdk.EventKey event) {
105+ int digit = ((char)event.keyval).digit_value ();
106+ unichar uc = Gdk.keyval_to_unicode (event.keyval);
107+ string? result = null;
108+ if (find_links < 0) {
109+ /* Links are currently off, turn them on */
110+ string css_style = """
111+ .%s, .%s {
112+ font-size:small !important;
113+ font-weight:bold !important;
114+ z-index: 777;
115+ border-radius:0.3em;
116+ line-height:1 !important;
117+ background: white !important;
118+ color: black !important;
119+ border: 1px solid gray;
120+ padding: 0 0.1em !important;
121+ position: absolute;
122+ display: inline !important;
123+ }
124+ .%s {
125+ padding: 0 0.1em 0.2em 0.1em !important;
126+ background: black !important;
127+ color: white !important;
128+ }
129+ """.printf (css_class_hint, css_class_key, css_class_key);
130+ inject_stylesheet (css_style);
131+ int label_count = 0;
132+ var dom = web_view.get_dom_document ();
133+ var links = dom.links;
134+ ulong nodes_length = links.length;
135+ for (ulong i = 0; i < nodes_length; i++) {
136+ var node = links.item (i);
137+ var element = node.parent_element;
138+ }
139+ }
140+#endif
141+#endif
142
143 public bool find (string text, bool case_sensitive, bool forward) {
144 #if HAVE_WEBKIT2
145@@ -238,12 +354,77 @@
146 // FIXME: mark matches, count matches, not found
147 return true;
148 #else
149+#if HAVE_DOM
150+ uint matches = 0;
151+ unmark_text_matches ();
152+ setup_styles ();
153+
154+ var dom = web_view.get_dom_document ();
155+ var nodes = dom.body.query_selector_all ("div,span,b,i,u,em,strong,p,input,button,h1,h2,h3,h4,h5,h6");
156+ var regex = new Regex ("(" + text + ")", case_sensitive ? 0 : RegexCompileFlags.CASELESS);
157+ ulong nodes_length = nodes.length;
158+ for (ulong i = 0; i < nodes_length; i++) {
159+ var node = nodes.item (i);
160+ ulong children_length = node.child_nodes.length;
161+ for (ulong j = 0; j < children_length; j++) {
162+ var child = node.child_nodes.item (j);
163+ if (child.node_type != 3) /* text node */
164+ continue;
165+
166+ /* Use text content to not match markup or scripts */
167+ string content = child.text_content;
168+ /* FIXME forward */
169+ bool match = regex.match (content);
170+ if (match) {
171+ matches++;
172+
173+ string inner_html;
174+ node.get ("inner-html", out inner_html);
175+ string new_html = regex.replace (inner_html, -1, 0,
176+ "<span class=\"%s\">\\1</span>".printf (css_class_match));
177+ node.set ("inner-html", new_html);
178+
179+ /* FIXME position
180+ var bubble = dom.create_element ("span");
181+ bubble.set_attribute ("class", css_class_match);
182+ bubble.append_child (dom.create_text_node (text));
183+ node.parent_node.insert_before (bubble, node); */
184+
185+ /* cf.
186+ var skip = 0;
187+ if (node.nodeType == 3) {
188+ var pos = node.data.toUpperCase().indexOf(pat);
189+ if (pos >= 0) {
190+ var spannode = document.createElement("span");
191+ spannode.className = "highlight";
192+ fbgcolor += ";padding: 0px; margin: 0px;";
193+ spannode.setAttribute("style", fbgcolor);
194+ var middlebit = node.splitText(pos);
195+ var endbit = middlebit.splitText(pat.length);
196+ var middleclone = middlebit.cloneNode(true);
197+ spannode.appendChild(middleclone);
198+ middlebit.parentNode.replaceChild(spannode, middlebit);
199+ skip = 1;
200+ }
201+ }
202+ else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
203+ for (var i = 0; i < node.childNodes.length; ++i)
204+ i += innerHighlight(node.childNodes[i], pat, fbgcolor);
205+ }
206+ return skip;
207+ */
208+ }
209+ }
210+ }
211+ return matches > 0;
212+#else
213 bool found = false;
214 found = web_view.search_text (text, case_sensitive, forward, true);
215 web_view.mark_text_matches (text, case_sensitive, 0);
216 web_view.set_highlight_text_matches (true);
217 return found;
218 #endif
219+#endif
220 }
221 }
222 }
223
224=== modified file 'midori/midori-view.c'
225--- midori/midori-view.c 2013-07-15 17:07:45 +0000
226+++ midori/midori-view.c 2013-07-15 22:57:23 +0000
227@@ -1756,30 +1756,8 @@
228 MidoriView* view)
229 {
230 #ifndef HAVE_WEBKIT2
231- gint digit = g_ascii_digit_value (event->keyval);
232- gunichar uc = gdk_keyval_to_unicode (event->keyval);
233- gchar* result = NULL;
234- WebKitWebFrame* web_frame = webkit_web_view_get_main_frame (web_view);
235- JSContextRef js_context = webkit_web_frame_get_global_context (web_frame);
236-
237- if (view->find_links < 0)
238 {
239- /* Links are currently off, turn them on */
240- midori_tab_inject_stylesheet (MIDORI_TAB (view), ".midoriHKD87346 {"
241- " font-size:small !important; font-weight:bold !important;"
242- " z-index:500; border-radius:0.3em; line-height:1 !important;"
243- " background: white !important; color: black !important;"
244- " border:1px solid gray; padding:0 0.1em !important;"
245- " position:absolute; display:inline !important; }");
246- midori_tab_inject_stylesheet (MIDORI_TAB (view), ".midori_access_key_fc04de {"
247- " font-size:small !important; font-weight:bold !important;"
248- " z-index:500; border-radius:0.3em; line-height:1 !important;"
249- " background: black !important; color: white !important;"
250- " border:1px solid gray; padding:0 0.1em 0.2em 0.1em !important;"
251- " position:absolute; display:inline !important; }");
252 result = sokoke_js_script_eval (js_context,
253- " var label_count = 0;"
254- " for (i in document.links) {"
255 " if (document.links[i].href && document.links[i].insertBefore) {"
256 " var child = document.createElement ('span');"
257 " if (document.links[i].accessKey && isNaN (document.links[i].accessKey)) {"
258
259=== modified file 'midori/webkitgtk-3.0.vapi'
260--- midori/webkitgtk-3.0.vapi 2012-12-16 18:40:10 +0000
261+++ midori/webkitgtk-3.0.vapi 2013-07-15 22:57:23 +0000
262@@ -158,6 +158,7 @@
263 public WebKit.DOMNode first_child { get; }
264 public WebKit.DOMNode last_child { get; }
265 public WebKit.DOMNode next_sibling { get; }
266+ public uint node_type { get; }
267 public WebKit.DOMElement parent_element { get; }
268 public WebKit.DOMNode parent_node { get; }
269 public WebKit.DOMNode previous_sibling { get; }
270
271=== modified file 'tests/tab.vala'
272--- tests/tab.vala 2013-04-04 21:23:44 +0000
273+++ tests/tab.vala 2013-07-15 22:57:23 +0000
274@@ -266,6 +266,30 @@
275 assert (!did_request_download);
276 }
277
278+void tab_find () {
279+ var browser = new Gtk.Window (Gtk.WindowType.TOPLEVEL);
280+ var tab = new Midori.View.with_title ();
281+ browser.add (tab);
282+ var loop = MainContext.default ();
283+ tab.set_uri ("http://.invalid");
284+ do { loop.iteration (true); } while (tab.load_status != Midori.LoadStatus.FINISHED);
285+ /* One button in the error page */
286+ assert (tab.find (_("Try again"), true, true) == 1);
287+#if HAVE_DOM
288+ /* The hostname occurs thrice */
289+ assert (tab.find (".invalid", true, true) == 3);
290+ /* Regex */
291+ assert (tab.find ("(in)?valid", true, true) == 3);
292+#endif
293+ /* Wrong case, shouldn't yield any matches */
294+ assert (tab.find ("http://.INValid", true, true) == 0);
295+ /* Not case sensitive */
296+ assert (tab.find ("http://.INValid", false, true) > 0);
297+ /* Lots of matches shouldn't pose a problem */
298+ assert (tab.find ("i", false, true) > 0);
299+ assert (tab.find ("n", false, true) > 0); //TODO: Berliner Weisse → Berliner Weiße
300+}
301+
302 void main (string[] args) {
303 Test.init (ref args);
304 Midori.App.setup (ref args, null);
305@@ -277,6 +301,7 @@
306 Test.add_func ("/tab/display-title", tab_display_title);
307 Test.add_func ("/tab/ellipsize", tab_display_ellipsize);
308 Test.add_func ("/tab/special", tab_special);
309+ Test.add_func ("/tab/find", tab_find);
310 Test.add_func ("/tab/alias", tab_alias);
311 Test.add_func ("/tab/http", tab_http);
312 Test.add_func ("/tab/movement", tab_movement);

Subscribers

People subscribed via source and target branches

to all changes: