2 (c) 2009 by Leon Winter
3 (c) 2009-2015 by Hannes Schueller
4 (c) 2009-2010 by Matto Fransen
5 (c) 2010-2011 by Hans-Peter Deifel
6 (c) 2010-2011 by Thomas Adam
8 (c) 2011-2014 by Daniel Carl
9 (c) 2012-2014 by Matthew Carter
10 (c) 2014 by Morgan Howe
11 (c) 2014 by Jan Niemeyer
17 #include <sys/types.h>
22 #include "vimprobable.h"
23 #include "utilities.h"
24 #include "callbacks.h"
25 #include "javascript.h"
27 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
28 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
29 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
31 /* remove unused bits, numlock symbol and buttons from keymask */
32 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
34 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
35 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
36 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
39 static void inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
);
40 static gboolean
inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
);
41 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
);
42 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
);
43 static WebKitWebView
* inspector_new_cb(WebKitWebInspector
* inspector
, WebKitWebView
* web_view
);
44 static gboolean
inspector_show_cb(WebKitWebInspector
*inspector
);
45 static gboolean
inspector_close_cb(WebKitWebInspector
*inspector
);
46 static void inspector_finished_cb(WebKitWebInspector
*inspector
);
47 static gboolean
notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
);
48 static gboolean
webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
);
49 static gboolean
webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
);
50 static void webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
);
51 static gboolean
webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
);
52 static void webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
53 static void webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
54 static gboolean
webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
55 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
56 static void webview_open_js_window_cb(WebKitWebView
*temp_view
, WebKitWebFrame
*frame
,
57 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*action
,
58 WebKitWebPolicyDecision
*policy
, gpointer data
);
59 static gboolean
webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
60 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
61 static WebKitWebView
* webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
62 static void webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
);
63 static void webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
);
64 static void window_destroyed_cb(GtkWidget
*window
, gpointer func_data
);
65 static gboolean
blank_cb(void);
68 static gboolean
bookmark(const Arg
*arg
);
69 static gboolean
browser_settings(const Arg
*arg
);
70 static gboolean
commandhistoryfetch(const Arg
*arg
);
71 static gboolean
complete(const Arg
*arg
);
72 static gboolean
descend(const Arg
*arg
);
73 gboolean
echo(const Arg
*arg
);
74 static gboolean
focus_input(const Arg
*arg
);
75 static gboolean
open_editor(const Arg
*arg
);
76 static gboolean
edit_source(const Arg
*arg
);
77 void _resume_from_editor(GPid child_pid
, int status
, gpointer data
);
78 void _resume_from_edit_source(GPid child_pid
, int status
, gpointer data
);
79 static gboolean
input(const Arg
*arg
);
80 static gboolean
open_inspector(const Arg
* arg
);
81 static gboolean
navigate(const Arg
*arg
);
82 static gboolean
number(const Arg
*arg
);
83 static gboolean
open_arg(const Arg
*arg
);
84 static gboolean
open_remembered(const Arg
*arg
);
85 static gboolean
paste(const Arg
*arg
);
86 static gboolean
quickmark(const Arg
*arg
);
87 static gboolean
quit(const Arg
*arg
);
88 static gboolean
revive(const Arg
*arg
);
89 static gboolean
print_frame(const Arg
*arg
);
90 static gboolean
search(const Arg
*arg
);
91 static gboolean
set(const Arg
*arg
);
92 static gboolean
script(const Arg
*arg
);
93 static gboolean
scroll(const Arg
*arg
);
94 static gboolean
search_tag(const Arg
*arg
);
95 static gboolean
yank(const Arg
*arg
);
96 static gboolean
view_source(const Arg
* arg
);
97 static gboolean
zoom(const Arg
*arg
);
98 static gboolean
fake_key_event(const Arg
*arg
);
100 static void clear_focus(void);
101 static void update_url(const char *uri
);
102 static void setup_client(void);
103 static void setup_modkeys(void);
104 static void setup_gui(void);
105 static void setup_settings(void);
106 static void setup_signals(void);
107 static void ascii_bar(int total
, int state
, char *string
);
108 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
109 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
110 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
111 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
112 const char *bg_color_str
, const char *fg_color_str
);
113 static void scripts_run_user_file(void);
114 static void show_link(const char *link
);
116 static gboolean
history(void);
117 static gboolean
process_set_line(char *line
);
118 void save_command_history(char *line
);
119 void toggle_proxy(gboolean onoff
);
120 void toggle_scrollbars(gboolean onoff
);
121 void set_default_winsize(const char * const size
);
123 gboolean
process_keypress(GdkEventKey
*event
);
124 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
125 GtkWidget
* fill_eventbox(const char * completion_line
);
126 static void mop_up(void);
136 /* Cookie support. */
137 #ifdef ENABLE_COOKIE_SUPPORT
138 static void setup_cookies(void);
139 static char *get_cookies(SoupURI
*soup_uri
);
140 static void load_all_cookies(void);
141 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
142 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
143 static void handle_response_headers(SoupMessage
*soup_msg
, gpointer unused
);
150 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
155 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
156 gtk_window_set_title(client
.gui
.window
, title
);
160 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
161 #ifdef ENABLE_GTK_PROGRESS_BAR
162 gtk_entry_set_progress_fraction(GTK_ENTRY(client
.gui
.inputbox
), progress
== 100 ? 0 : (double)progress
/100);
167 #ifdef ENABLE_WGET_PROGRESS_BAR
169 ascii_bar(int total
, int state
, char *string
) {
172 for (i
= 0; i
< state
; i
++)
173 string
[i
] = progressbartickchar
;
174 string
[i
++] = progressbarcurrent
;
175 for (; i
< total
; i
++)
176 string
[i
] = progressbarspacer
;
182 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
183 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
184 const char *uri
= webkit_web_view_get_uri(webview
);
189 scripts_run_user_file();
191 if (client
.state
.mode
== ModeInsert
|| client
.state
.mode
== ModeHints
) {
192 Arg a
= { .i
= ModeNormal
};
195 client
.state
.manual_focus
= FALSE
;
199 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
200 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
203 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
204 if (escape_input_on_load
&& scripts
&& !client
.state
.manual_focus
&& !gtk_widget_is_focus(client
.gui
.inputbox
)) {
207 if (HISTORY_MAX_ENTRIES
> 0)
213 webview_open_js_window_cb(WebKitWebView
*temp_view
, WebKitWebFrame
*frame
,
214 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*action
,
215 WebKitWebPolicyDecision
*policy
, gpointer data
) {
216 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
)};
217 /* open the requested window */
220 gtk_widget_destroy(GTK_WIDGET(temp_view
));
223 static WebKitWebView
*
224 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
225 /* create a temporary webview to execute the script in */
226 WebKitWebView
*temp_view
= WEBKIT_WEB_VIEW(webkit_web_view_new());
227 /* wait until the new webview receives its new URI */
228 g_signal_connect(temp_view
, "navigation-policy-decision-requested", G_CALLBACK(webview_open_js_window_cb
), NULL
);
233 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
234 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
235 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
237 webkit_web_policy_decision_ignore(decision
);
242 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
243 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
244 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
245 if (SOUP_STATUS_IS_SUCCESSFUL(client
.net
.http_status
)) {
246 webkit_web_policy_decision_download(decision
);
255 static WebKitWebView
*
256 inspector_new_cb(WebKitWebInspector
*inspector
, WebKitWebView
* web_view
) {
257 return WEBKIT_WEB_VIEW(webkit_web_view_new());
261 inspector_show_cb(WebKitWebInspector
*inspector
) {
262 WebKitWebView
*webview
;
263 State
*state
= &client
.state
;
265 if (state
->is_inspecting
) {
269 webview
= webkit_web_inspector_get_web_view(inspector
);
270 gtk_paned_pack2(GTK_PANED(client
.gui
.pane
), GTK_WIDGET(webview
), TRUE
, TRUE
);
271 gtk_widget_show(GTK_WIDGET(webview
));
273 state
->is_inspecting
= TRUE
;
279 inspector_close_cb(WebKitWebInspector
*inspector
) {
281 State
*state
= &client
.state
;
283 if (!state
->is_inspecting
) {
286 widget
= GTK_WIDGET(webkit_web_inspector_get_web_view(inspector
));
287 gtk_widget_hide(widget
);
288 gtk_widget_destroy(widget
);
290 state
->is_inspecting
= FALSE
;
296 inspector_finished_cb(WebKitWebInspector
*inspector
) {
301 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
302 const gchar
*filename
;
305 WebKitDownloadStatus status
;
307 filename
= webkit_download_get_suggested_filename(download
);
308 if (filename
== NULL
|| strlen(filename
) == 0) {
309 filename
= "vimprobable_download";
311 path
= g_build_filename(downloads_path
, filename
, NULL
);
312 uri
= g_strconcat("file://", path
, NULL
);
313 webkit_download_set_destination_uri(download
, uri
);
316 size
= (uint32_t)webkit_download_get_total_size(download
);
318 echo_message(Info
, "Download %s started (expected size: %u bytes)...", filename
, size
);
320 echo_message(Info
, "Download %s started (unknown size)...", filename
);
321 client
.state
.activeDownloads
= g_list_prepend(client
.state
.activeDownloads
, download
);
322 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
323 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
324 status
= webkit_download_get_status(download
);
325 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
326 webkit_download_start(download
);
337 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
338 WebKitDownloadStatus status
= webkit_download_get_status(d
);
340 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
341 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
342 echo_message(Error
, "Error while downloading %s", webkit_download_get_suggested_filename(d
));
344 echo_message(Info
, "Download %s finished", webkit_download_get_suggested_filename(d
));
346 client
.state
.activeDownloads
= g_list_remove(client
.state
.activeDownloads
, d
);
353 process_keypress(GdkEventKey
*event
) {
354 State
*state
= &client
.state
;
357 GdkModifierType irrelevant
;
359 /* Get a mask of modifiers that shouldn't be considered for this event.
360 * E.g.: It shouldn't matter whether ';' is shifted or not. */
361 gdk_keymap_translate_keyboard_state(state
->keymap
, event
->hardware_keycode
,
362 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
364 current
= client
.config
.keylistroot
;
366 while (current
!= NULL
) {
367 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
368 && (current
->Element
.modkey
== state
->current_modkey
369 || (!current
->Element
.modkey
&& !state
->current_modkey
)
370 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
371 && current
->Element
.key
== keyval
372 && current
->Element
.func
)
373 if (current
->Element
.func(¤t
->Element
.arg
)) {
374 state
->current_modkey
= state
->count
= 0;
378 current
= current
->next
;
384 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
385 State
*state
= &client
.state
;
386 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
388 GdkModifierType irrelevant
;
390 /* Get a mask of modifiers that shouldn't be considered for this event.
391 * E.g.: It shouldn't matter whether ';' is shifted or not. */
392 gdk_keymap_translate_keyboard_state(state
->keymap
, event
->hardware_keycode
,
393 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
395 switch (state
->mode
) {
397 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
398 if (IS_ESCAPE(event
)) {
399 echo_message(Info
, "");
401 } else if (state
->current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
402 || (event
->keyval
== GDK_0
&& state
->count
))) {
403 state
->count
= (state
->count
? state
->count
* 10 : 0) + (event
->keyval
- GDK_0
);
406 } else if (strchr(client
.config
.modkeys
, event
->keyval
) && state
->current_modkey
!= event
->keyval
) {
407 state
->current_modkey
= event
->keyval
;
413 if (process_keypress(event
) == TRUE
) return TRUE
;
417 if (IS_ESCAPE(event
)) {
419 a
.s
= g_strdup("hints.clearFocus();");
424 } else if (CLEAN(event
->state
) & GDK_CONTROL_MASK
) {
425 /* keybindings of non-printable characters */
426 if (process_keypress(event
) == TRUE
) return TRUE
;
428 case ModePassThrough
:
429 if (IS_ESCAPE(event
)) {
430 echo_message(Info
, "");
436 echo_message(Info
, "");
444 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
445 const char *fg_color_str
) {
448 PangoFontDescription
*font
;
450 font
= pango_font_description_from_string(font_str
);
451 gtk_widget_modify_font(widget
, font
);
452 pango_font_description_free(font
);
455 gdk_color_parse(fg_color_str
, &fg_color
);
457 gdk_color_parse(bg_color_str
, &bg_color
);
459 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
460 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
466 show_link(const char *link
) {
469 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
470 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_url
), markup
);
471 strncpy(client
.state
.rememberedURI
, link
, BUF_SIZE
);
476 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
477 const char *uri
= webkit_web_view_get_uri(webview
);
479 memset(client
.state
.rememberedURI
, 0, BUF_SIZE
);
487 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
490 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
491 if (gtk_window_has_toplevel_focus(client
.gui
.window
)) {
492 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
495 } else if (!strcmp(message
, "insertmode_on")) {
504 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
505 Gui
*gui
= &client
.gui
;
506 State
*state
= &client
.state
;
508 guint16 length
= gtk_entry_get_text_length(entry
);
510 gboolean forward
= FALSE
;
512 a
.i
= HideCompletion
;
516 text
= (char*)gtk_entry_get_text(entry
);
518 /* move focus from inputbox to print potential messages that could not be
519 * printed as long as the inputbox is focused */
520 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
522 if (length
> 1 && text
[0] == ':') {
523 process_line((text
+ 1));
524 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
525 webkit_web_view_unmark_text_matches(gui
->webview
);
526 #ifdef ENABLE_MATCH_HIGHLITING
527 webkit_web_view_mark_text_matches(gui
->webview
, &text
[1], FALSE
, 0);
528 webkit_web_view_set_highlight_text_matches(gui
->webview
, TRUE
);
531 #ifndef ENABLE_INCREMENTAL_SEARCH
533 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
536 state
->search_direction
= forward
;
537 if (state
->search_handle
) {
538 g_free(state
->search_handle
);
540 state
->search_handle
= g_strdup(&text
[1]);
542 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
544 a
.s
= g_strdup_printf("hints.fire();");
550 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
554 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
557 State
*state
= &client
.state
;
559 if (state
->mode
== ModeHints
) {
560 if (event
->keyval
== GDK_Tab
) {
562 a
.s
= g_strdup_printf("hints.focusNextHint();");
568 if (event
->keyval
== GDK_ISO_Left_Tab
) {
570 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
576 if (event
->keyval
== GDK_Return
) {
578 a
.s
= g_strdup_printf("hints.fire();");
585 switch (event
->keyval
) {
586 case GDK_bracketleft
:
588 if (!IS_ESCAPE(event
)) break;
589 a
.i
= HideCompletion
;
592 state
->commandpointer
= 0;
601 return commandhistoryfetch(&a
);
605 return commandhistoryfetch(&a
);
607 case GDK_ISO_Left_Tab
:
613 if (state
->mode
== ModeHints
) {
614 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
615 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
616 (event
->keyval
== GDK_BackSpace
)) {
619 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
626 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
627 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && state
->count
)) {
628 /* allow a zero as non-first number */
629 state
->count
= (state
->count
? state
->count
* 10 : 0) + numval
;
631 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
643 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
645 WebKitHitTestResult
*result
;
646 WebKitHitTestResultContext context
;
647 State
*state
= &client
.state
;
648 if (state
->mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
649 /* handle mouse click events */
650 for (i
= 0; i
< LENGTH(mouse
); i
++) {
651 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
652 && (mouse
[i
].modkey
== state
->current_modkey
653 || (!mouse
[i
].modkey
&& !state
->current_modkey
)
654 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
655 && mouse
[i
].button
== event
->button
.button
657 if (mouse
[i
].func(&mouse
[i
].arg
)) {
658 state
->current_modkey
= state
->count
= 0;
664 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
665 g_object_get(result
, "context", &context
, NULL
);
666 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
667 Arg a
= { .i
= ModeInsert
};
669 state
->manual_focus
= TRUE
;
671 } else if (state
->mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
672 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
673 g_object_get(result
, "context", &context
, NULL
);
674 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
675 Arg a
= { .i
= ModeNormal
};
679 gchar
*value
= NULL
, *message
= NULL
;
680 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
681 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
682 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
684 state
->manual_focus
= TRUE
;
692 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
694 guint16 length
= gtk_entry_get_text_length(entry
);
697 a
.i
= HideCompletion
;
705 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
707 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
708 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
709 gboolean forward
= FALSE
;
711 /* Update incremental search if the user changes the search text.
713 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
714 * from the user. But if the entry is focused and the text is set
715 * through gtk_entry_set_text() in some asyncrounous operation,
716 * I would consider that a bug.
719 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
720 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
721 webkit_web_view_search_text(client
.gui
.webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
723 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
724 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
728 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
732 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
739 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
742 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
745 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
748 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
750 case 'O': case 'T': case 'W':
751 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
754 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
757 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
760 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'l');", NULL
);
765 client
.state
.count
= 0;
772 } else if (length
== 0) {
773 client
.state
.mode
= ModeNormal
;
775 a
.s
= g_strdup("hints.clearHints();");
778 client
.state
.count
= 0;
787 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
788 memset(suggline
, 0, 512);
789 strncpy(suggline
, command
, 512);
790 strncat(suggline
, " ", 1);
791 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
794 GtkWidget
* fill_eventbox(const char * completion_line
) {
796 GtkWidget
*row_eventbox
, *el
;
798 char *markup
, *markup_tmp
;
800 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
801 row_eventbox
= gtk_event_box_new();
802 gdk_color_parse(completionbgcolor
[0], &color
);
803 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
804 el
= gtk_label_new(NULL
);
805 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
806 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
807 markup_tmp
, "</span>", NULL
);
808 gtk_label_set_markup(GTK_LABEL(el
), markup
);
811 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
812 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
813 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
818 complete(const Arg
*arg
) {
819 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
820 size_t listlen
, len
, cmdlen
;
822 Listelement
*elementlist
= NULL
, *elementpointer
;
823 gboolean highlight
= FALSE
;
825 GtkWidget
*row_eventbox
, *el
;
828 static GtkWidget
*table
, *top_border
;
830 static char **suggestions
;
831 static GtkWidget
**widgets
;
832 static int n
= 0, m
, current
= -1;
833 Gui
*gui
= &client
.gui
;
835 str
= (char*)gtk_entry_get_text(GTK_ENTRY(gui
->inputbox
));
838 /* Get the length of the list of commands for completion. We need this to
839 * malloc/realloc correctly.
841 listlen
= LENGTH(commands
);
843 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
846 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
847 gdk_color_parse(completionbgcolor
[0], &color
);
848 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
849 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
850 if ((arg
->i
== DirectionNext
&& current
== 0)
851 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
857 gtk_widget_destroy(GTK_WIDGET(table
));
858 gtk_widget_destroy(GTK_WIDGET(top_border
));
865 if (arg
->i
== HideCompletion
)
868 } else if (arg
->i
== HideCompletion
)
871 prefix
= g_strdup(str
);
872 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
873 suggestions
= malloc(sizeof(char*) * listlen
);
874 top_border
= gtk_event_box_new();
875 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
876 gdk_color_parse(completioncolor
[2], &color
);
877 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
878 table
= gtk_event_box_new();
879 gdk_color_parse(completionbgcolor
[0], &color
);
880 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
882 if (strchr(str
, ' ') == NULL
) {
883 /* command completion */
884 listlen
= LENGTH(commands
);
885 for (i
= 0; i
< listlen
; i
++) {
886 if (commands
[i
].cmd
== NULL
)
888 cmdlen
= strlen(commands
[i
].cmd
);
889 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
890 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
892 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
893 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
894 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
895 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
897 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
898 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
899 row_eventbox
= gtk_event_box_new();
900 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
901 el
= gtk_label_new(NULL
);
902 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
904 gtk_label_set_markup(GTK_LABEL(el
), markup
);
906 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
907 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
908 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
909 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
910 suggestions
[n
] = commands
[i
].cmd
;
911 widgets
[n
++] = row_eventbox
;
915 entry
= (char *)malloc(512 * sizeof(char));
919 memset(entry
, 0, 512);
920 suggurls
= malloc(sizeof(char*) * listlen
);
921 if (suggurls
== NULL
) {
924 spacepos
= strcspn(str
, " ");
925 searchfor
= (str
+ spacepos
+ 1);
926 strncpy(command
, (str
+ 1), spacepos
- 1);
927 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
928 /* browser settings */
929 listlen
= LENGTH(browsersettings
);
930 for (i
= 0; i
< listlen
; i
++) {
931 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
933 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
934 /* FIXME(HP): This memory is never freed */
935 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
936 strncpy(suggurls
[n
], suggline
, 512);
937 suggestions
[n
] = suggurls
[n
];
938 row_eventbox
= fill_eventbox(suggline
);
939 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
940 widgets
[n
++] = row_eventbox
;
944 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
945 /* completion on tags */
946 spacepos
= strcspn(str
, " ");
947 searchfor
= (str
+ spacepos
+ 1);
948 elementlist
= complete_list(searchfor
, 1, elementlist
);
950 /* URL completion: bookmarks */
951 elementlist
= complete_list(searchfor
, 0, elementlist
);
952 m
= count_list(elementlist
);
953 if (m
< MAX_LIST_SIZE
) {
954 /* URL completion: history */
955 elementlist
= complete_list(searchfor
, 2, elementlist
);
958 elementpointer
= elementlist
;
959 while (elementpointer
!= NULL
) {
960 fill_suggline(suggline
, command
, elementpointer
->element
);
961 /* FIXME(HP): This memory is never freed */
962 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
963 strncpy(suggurls
[n
], suggline
, 512);
964 suggestions
[n
] = suggurls
[n
];
965 row_eventbox
= fill_eventbox(suggline
);
966 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
967 widgets
[n
++] = row_eventbox
;
968 elementpointer
= elementpointer
->next
;
969 if (n
>= MAX_LIST_SIZE
)
972 free_list(elementlist
);
973 if (suggurls
!= NULL
) {
982 /* TA: FIXME - this needs rethinking entirely. */
984 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
985 if (widgets_temp
== NULL
&& widgets
== NULL
) {
986 fprintf(stderr
, "Couldn't realloc() widgets\n");
989 widgets
= widgets_temp
;
990 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
991 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
992 fprintf(stderr
, "Couldn't realloc() suggestions\n");
995 suggestions
= suggestions_temp
;
998 gdk_color_parse(completionbgcolor
[1], &color
);
999 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
1000 el
= gtk_label_new(NULL
);
1001 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
1002 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
1003 gtk_label_set_markup(GTK_LABEL(el
), markup
);
1005 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
1007 gtk_box_pack_start(gui
->box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
1008 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
1009 gtk_box_pack_start(gui
->box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
1010 gtk_widget_show_all(GTK_WIDGET(table
));
1011 gtk_widget_show_all(GTK_WIDGET(top_border
));
1014 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
1016 if (current
!= -1) {
1017 gdk_color_parse(completionbgcolor
[2], &color
);
1018 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
1019 s
= g_strconcat(":", suggestions
[current
], NULL
);
1020 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), s
);
1023 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), prefix
);
1024 gtk_editable_set_position(GTK_EDITABLE(gui
->inputbox
), -1);
1029 descend(const Arg
*arg
) {
1030 char *source
= (char*)webkit_web_view_get_uri(client
.gui
.webview
), *p
= &source
[0], *new;
1032 client
.state
.count
= client
.state
.count
? client
.state
.count
: 1;
1036 if (arg
->i
== Rootdir
) {
1037 for (i
= 0; i
< 3; i
++) /* get to the third slash */
1038 if (!(p
= strchr(++p
, '/')))
1039 return TRUE
; /* if we cannot find it quit */
1041 len
= strlen(source
);
1042 if (!len
) /* if string is empty quit */
1044 p
= source
+ len
; /* start at the end */
1045 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1046 ++client
.state
.count
;
1047 for (i
= 0; i
< client
.state
.count
; i
++)
1048 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1049 if (p
== source
) /* if we reach the first char pointer quit */
1051 ++p
; /* since we do p-- in the while, we are pointing at
1052 the char before the slash, so +1 */
1054 len
= p
- source
+ 1; /* new length = end - start + 1 */
1055 new = malloc(len
+ 1);
1056 memcpy(new, source
, len
);
1058 webkit_web_view_load_uri(client
.gui
.webview
, new);
1064 echo(const Arg
*arg
) {
1065 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1067 if (index
< Info
|| index
> Error
)
1070 if (!gtk_widget_is_focus(GTK_WIDGET(client
.gui
.inputbox
))) {
1071 set_widget_font_and_color(client
.gui
.inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1072 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), !arg
->s
? "" : arg
->s
);
1079 open_inspector(const Arg
* arg
) {
1080 gboolean inspect_enabled
;
1081 WebKitWebSettings
*settings
;
1082 State
*state
= &client
.state
;
1084 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
1085 g_object_get(G_OBJECT(settings
), "enable-developer-extras", &inspect_enabled
, NULL
);
1086 if (inspect_enabled
) {
1087 if (state
->is_inspecting
) {
1088 webkit_web_inspector_close(client
.gui
.inspector
);
1090 webkit_web_inspector_show(client
.gui
.inspector
);
1094 echo_message(Error
, "Webinspector is not enabled");
1100 input(const Arg
*arg
) {
1102 client
.state
.count
= 0;
1106 GtkWidget
*inputbox
= client
.gui
.inputbox
;
1108 /* if inputbox hidden, show it again */
1109 if (!gtk_widget_get_visible(inputbox
))
1110 gtk_widget_set_visible(inputbox
, TRUE
);
1114 /* Set the colour and font back to the default, so that we don't still
1115 * maintain a red colour from a warning from an end of search indicator,
1118 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1120 /* to avoid things like :open URL :open URL2 or :open :open URL */
1121 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1122 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1123 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(client
.gui
.webview
)))
1124 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1126 gtk_widget_grab_focus(inputbox
);
1127 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1129 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1130 client
.state
.mode
= ModeHints
;
1132 switch (arg
->s
[0]) {
1134 a
.s
= g_strdup("hints.createHints('', 'f');");
1138 a
.s
= g_strdup("hints.createHints('', 'F');");
1144 switch (arg
->s
[1]) {
1146 a
.s
= g_strdup("hints.createHints('', 's');");
1149 a
.s
= g_strdup("hints.createHints('', 'y');");
1152 a
.s
= g_strdup("hints.createHints('', 'f');");
1155 a
.s
= g_strdup("hints.createHints('', 'F');");
1157 case 'O': case 'T': case 'W':
1158 a
.s
= g_strdup("hints.createHints('', 'O');");
1161 a
.s
= g_strdup("hints.createHints('', 'i');");
1164 a
.s
= g_strdup("hints.createHints('', 'I');");
1167 a
.s
= g_strdup("hints.createHints('', 'l');");
1173 client
.state
.count
= 0;
1184 navigate(const Arg
*arg
) {
1185 if (arg
->i
& NavigationForwardBack
)
1186 webkit_web_view_go_back_or_forward(client
.gui
.webview
, (arg
->i
== NavigationBack
? -1 : 1) * (client
.state
.count
? client
.state
.count
: 1));
1187 else if (arg
->i
& NavigationReloadActions
)
1188 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(client
.gui
.webview
);
1190 webkit_web_view_stop_loading(client
.gui
.webview
);
1195 number(const Arg
*arg
) {
1196 const char *source
= webkit_web_view_get_uri(client
.gui
.webview
);
1197 char *uri
, *p
, *new;
1198 int number
, diff
= (client
.state
.count
? client
.state
.count
: 1) * (arg
->i
== Increment
? 1 : -1);
1202 uri
= g_strdup(source
); /* copy string */
1204 while(*p
!= '\0') /* goto the end of the string */
1207 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1209 if (*(++p
) == '\0') { /* if no numbers were found abort */
1213 number
= atoi(p
) + diff
; /* apply diff on number */
1215 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1216 webkit_web_view_load_uri(client
.gui
.webview
, new);
1223 open_arg(const Arg
*arg
) {
1225 char *s
= arg
->s
, *p
= NULL
, *new;
1226 Arg a
= { .i
= NavigationReload
};
1228 const char *search_uri
;
1230 struct stat statbuf
;
1232 if (client
.state
.embed
) {
1234 snprintf(winid
, LENGTH(winid
), "%u", (gint
)client
.state
.embed
);
1248 else if (arg
->i
== TargetCurrent
) {
1249 while(*s
== ' ') /* strip leading whitespace */
1251 p
= (s
+ strlen(s
) - 1);
1252 while(*p
== ' ') /* strip trailing whitespace */
1257 /* check for external handlers */
1258 if (open_handler(s
))
1260 /* check for search engines */
1263 /* shortcut without search term */
1266 /* search term given */
1270 search_uri
= find_uri_for_searchengine(s
);
1271 if (search_uri
!= NULL
) {
1273 search_term
= soup_uri_encode(p
+1, "&");
1274 new = g_strdup_printf(search_uri
, search_term
);
1275 g_free(search_term
);
1277 if (!strstr(search_uri
, "%s"))
1278 new = g_strdup(search_uri
);
1280 /* the search engine definition expected an argument */
1281 new = g_strdup_printf(search_uri
, "");
1288 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1289 p
= new = g_malloc(len
+ 1);
1290 while(*s
!= '\0') { /* strip whitespaces */
1296 } else if (!stat(s
, &statbuf
)) { /* prepend "file://" */
1297 char *rpath
= realpath(s
, NULL
);
1298 if (rpath
!= NULL
) {
1299 len
= strlen(rpath
);
1300 new = g_malloc(sizeof("file://") + len
);
1301 sprintf(new, "file://%s", rpath
);
1304 new = g_malloc(sizeof("file://") + len
);
1305 sprintf(new, "file://%s", s
);
1307 } else if (space
> 0 || !strchr(s
, '.')) { /* whitespaces or no dot? */
1308 search_uri
= find_uri_for_searchengine(defaultsearch
);
1309 if (search_uri
!= NULL
) {
1310 search_term
= soup_uri_encode(s
, "&");
1311 new = g_strdup_printf(search_uri
, search_term
);
1312 g_free(search_term
);
1314 } else { /* prepend "http://" */
1315 new = g_malloc(sizeof("http://") + len
);
1316 strcpy(new, "http://");
1317 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1320 webkit_web_view_load_uri(client
.gui
.webview
, new);
1323 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1328 open_remembered(const Arg
*arg
)
1330 Arg a
= {arg
->i
, client
.state
.rememberedURI
};
1332 if (strcmp(client
.state
.rememberedURI
, "")) {
1339 yank(const Arg
*arg
) {
1340 const char *url
, *content
;
1342 if (arg
->i
& SourceSelection
) {
1343 webkit_web_view_copy_clipboard(client
.gui
.webview
);
1344 if (arg
->i
& ClipboardPrimary
)
1345 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1346 if (!content
&& arg
->i
& ClipboardGTK
)
1347 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1349 echo_message(Info
, "Yanked %s", content
);
1350 g_free((gpointer
*)content
);
1353 if (arg
->i
& SourceURL
) {
1354 url
= webkit_web_view_get_uri(client
.gui
.webview
);
1361 echo_message(Info
, "Yanked %s", url
);
1362 if (arg
->i
& ClipboardPrimary
)
1363 gtk_clipboard_set_text(client
.state
.clipboards
[0], url
, -1);
1364 if (arg
->i
& ClipboardGTK
)
1365 gtk_clipboard_set_text(client
.state
.clipboards
[1], url
, -1);
1371 paste(const Arg
*arg
) {
1372 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1374 /* If we're over a link, open it in a new target. */
1375 if (strlen(client
.state
.rememberedURI
) > 0) {
1376 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1377 open_arg(&new_target
);
1381 if (arg
->i
& ClipboardPrimary
)
1382 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1383 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1384 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1393 quit(const Arg
*arg
) {
1395 const char *filename
;
1396 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1398 /* write last URL into status file for recreation with "u" */
1399 filename
= g_strdup_printf("%s", client
.config
.config_base
);
1400 filename
= g_strdup_printf(private_mode
? "/dev/null" : CLOSED_URL_FILENAME
);
1401 f
= fopen(filename
, "w");
1402 g_free((gpointer
*)filename
);
1404 fprintf(f
, "%s", uri
);
1413 revive(const Arg
*arg
) {
1415 const char *filename
;
1416 char buffer
[512] = "";
1417 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1418 /* get the URL of the window which has been closed last */
1419 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1420 f
= fopen(filename
, "r");
1421 g_free((gpointer
*)filename
);
1423 fgets(buffer
, 512, f
);
1426 if (strlen(buffer
) > 0) {
1435 gboolean
print_frame(const Arg
*arg
)
1437 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1438 webkit_web_frame_print (frame
);
1443 search(const Arg
*arg
) {
1444 State
*state
= &client
.state
;
1445 state
->count
= state
->count
? state
->count
: 1;
1446 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1449 if (state
->search_handle
) {
1450 g_free(state
->search_handle
);
1452 state
->search_handle
= g_strdup(arg
->s
);
1454 if (!state
->search_handle
)
1456 if (arg
->i
& DirectionAbsolute
)
1457 state
->search_direction
= direction
;
1459 direction
^= state
->search_direction
;
1461 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1463 if (arg
->i
& Wrapping
) {
1464 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1466 echo_message(Warning
, "search hit %s, continuing at %s",
1467 direction
? "BOTTOM" : "TOP",
1468 direction
? "TOP" : "BOTTOM");
1474 } while(--state
->count
);
1476 echo_message(Error
, "Pattern not found: %s", state
->search_handle
);
1482 set(const Arg
*arg
) {
1485 if (client
.state
.search_handle
) {
1486 g_free(client
.state
.search_handle
);
1487 client
.state
.search_handle
= NULL
;
1488 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
1490 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), "");
1491 gtk_widget_grab_focus(GTK_WIDGET(client
.gui
.webview
));
1493 case ModePassThrough
:
1494 echo_message(Info
| NoAutoHide
, "-- PASS THROUGH --");
1497 echo_message(Info
| NoAutoHide
, "-- PASS TROUGH (next) --");
1499 case ModeInsert
: /* should not be called manually but automatically */
1500 /* make sure we leaf focus from inputbox to show the new mode */
1501 gtk_widget_grab_focus(GTK_WIDGET(client
.gui
.webview
));
1502 echo_message(Info
| NoAutoHide
, "-- INSERT --");
1507 client
.state
.mode
= arg
->i
;
1512 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1513 JSStringRef string_ref
;
1517 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1518 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1519 string
= g_new(gchar
, length
);
1520 JSStringGetUTF8CString(string_ref
, string
, length
);
1521 JSStringRelease(string_ref
);
1526 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1527 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1528 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1530 JSValueRef val
, exception
;
1532 str
= JSStringCreateWithUTF8CString(script
);
1533 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1534 JSStringRelease(str
);
1536 *message
= jsapi_ref_to_string(context
, exception
);
1538 *value
= jsapi_ref_to_string(context
, val
);
1542 quickmark(const Arg
*a
) {
1545 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1547 fp
= fopen(fn
, "r");
1552 if (fp
!= NULL
&& b
< 128) {
1553 for( i
=0; i
< b
; ++i
) {
1557 fgets(buf
, 100, fp
);
1559 char *ptr
= strrchr(buf
, '\n');
1562 Arg x
= { .s
= buf
, .i
= a
->i
? TargetNew
: TargetCurrent
};
1563 return open_arg(&x
);
1565 echo_message(Error
, "Quickmark %c not defined", (char) b
);
1568 } else { return false; }
1572 script(const Arg
*arg
) {
1573 gchar
*value
= NULL
, *message
= NULL
;
1574 char text
[BUF_SIZE
] = "";
1576 WebKitNetworkRequest
*request
;
1577 WebKitDownload
*download
;
1580 set_error("Missing argument.");
1583 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1591 if (arg
->i
!= Silent
&& value
) {
1592 echo_message(arg
->i
, value
);
1594 /* switch mode according to scripts return value */
1596 if (strncmp(value
, "done;", 5) == 0) {
1599 } else if (strncmp(value
, "insert;", 7) == 0) {
1602 client
.state
.manual_focus
= TRUE
;
1603 } else if (strncmp(value
, "save;", 5) == 0) {
1604 /* forced download */
1607 request
= webkit_network_request_new((value
+ 5));
1608 download
= webkit_download_new(request
);
1609 webview_download_cb(client
.gui
.webview
, download
, (gpointer
*)NULL
);
1610 } else if (strncmp(value
, "yank;", 5) == 0) {
1611 /* yank link URL to clipboard */
1614 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1617 } else if (strncmp(value
, "colon;", 6) == 0) {
1618 /* use link URL for colon command */
1619 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(client
.gui
.inputbox
)), 1023);
1624 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1627 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1634 } else if (strncmp(value
, "open;", 5) == 0 || strncmp(value
, "tabopen;", 8) == 0) {
1635 /* TODO: open element */
1638 if (strncmp(value
, "open;", 5) == 0)
1639 a
.i
= TargetCurrent
;
1642 a
.s
= (strchr(value
, ';') + 1);
1644 } else if (strncmp(value
, "show_link;", 10) == 0) {
1647 char *link
= strchr(value
, ';') + 1;
1649 memset(client
.state
.rememberedURI
, 0, BUF_SIZE
);
1652 } else if (strncmp(value
, "error;", 6) == 0) {
1662 scroll(const Arg
*arg
) {
1663 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? client
.gui
.adjust_h
: client
.gui
.adjust_v
;
1664 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1665 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1666 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1667 unsigned int count
= client
.state
.count
;
1669 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1670 if (arg
->i
& ScrollMove
)
1671 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1672 direction
* /* direction */
1673 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1674 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1675 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1676 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1678 gtk_adjustment_set_value(adjust
,
1679 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1686 zoom(const Arg
*arg
) {
1687 unsigned int count
= client
.state
.count
;
1689 webkit_web_view_set_full_content_zoom(client
.gui
.webview
, (arg
->i
& ZoomFullContent
) > 0);
1690 webkit_web_view_set_zoom_level(client
.gui
.webview
, (arg
->i
& ZoomOut
) ?
1691 webkit_web_view_get_zoom_level(client
.gui
.webview
) +
1692 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * client
.config
.zoomstep
) :
1693 (count
? (float)count
/ 100.0 : 1.0));
1698 fake_key_event(const Arg
*a
) {
1699 if(!client
.state
.embed
) {
1703 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1704 echo_message(Error
, "Couldn't find the XDisplay.");
1710 xk
.subwindow
= None
;
1711 xk
.time
= CurrentTime
;
1712 xk
.same_screen
= True
;
1713 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1714 xk
.window
= client
.state
.embed
;
1718 echo_message(Error
, "Zero pointer as argument! Check your config.h");
1723 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1724 echo_message(Error
, "Couldn't translate %s to keysym", a
->s
);
1728 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1729 echo_message(Error
, "Couldn't translate keysym to keycode");
1734 if( !XSendEvent(xdpy
, client
.state
.embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1735 echo_message(Error
, "XSendEvent failed");
1744 commandhistoryfetch(const Arg
*arg
) {
1745 State
*state
= &client
.state
;
1746 const int length
= g_list_length(client
.state
.commandhistory
);
1747 gchar
*input_message
= NULL
;
1750 if (arg
->i
== DirectionPrev
) {
1751 state
->commandpointer
= (length
+ state
->commandpointer
- 1) % length
;
1753 state
->commandpointer
= (length
+ state
->commandpointer
+ 1) % length
;
1756 const char* command
= (char *)g_list_nth_data(state
->commandhistory
, state
->commandpointer
);
1757 input_message
= g_strconcat(":", command
, NULL
);
1758 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), input_message
);
1759 g_free(input_message
);
1760 gtk_editable_set_position(GTK_EDITABLE(client
.gui
.inputbox
), -1);
1768 bookmark(const Arg
*arg
) {
1770 const char *filename
;
1771 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1772 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1773 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1774 f
= fopen(filename
, "a");
1775 g_free((gpointer
*)filename
);
1776 if (uri
== NULL
|| strlen(uri
) == 0) {
1777 set_error("No URI found to bookmark.");
1781 fprintf(f
, "%s", uri
);
1782 if (title
!= NULL
) {
1783 fprintf(f
, "%s", " ");
1784 fprintf(f
, "%s", title
);
1786 if (arg
->s
&& strlen(arg
->s
)) {
1787 build_taglist(arg
, f
);
1789 fprintf(f
, "%s", "\n");
1791 echo_message(Info
, "Bookmark saved");
1794 set_error("Bookmarks file not found.");
1802 const char *filename
;
1803 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1804 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1805 char *entry
, buffer
[512], *new;
1807 gboolean finished
= FALSE
;
1809 if (title
!= NULL
) {
1810 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1811 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1813 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1814 memset(entry
, 0, strlen(uri
) + 1);
1816 if (entry
!= NULL
) {
1817 strncpy(entry
, uri
, strlen(uri
));
1818 if (title
!= NULL
) {
1819 strncat(entry
, " ", 1);
1820 strncat(entry
, title
, strlen(title
));
1823 filename
= g_strdup_printf(private_mode
? "/dev/null" : HISTORY_STORAGE_FILENAME
);
1824 f
= fopen(filename
, "r");
1826 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1828 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1829 /* newest entries go on top */
1830 strncpy(new, entry
, strlen(entry
));
1831 strncat(new, "\n", 1);
1832 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1833 while (finished
!= TRUE
) {
1834 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1835 /* check if end of file was reached / error occured */
1839 /* end of file reached */
1843 /* compare line (-1 because of newline character) */
1844 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1845 /* if the URI is already in history; we put it on top and skip it here */
1846 strncat(new, buffer
, 512);
1849 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1855 f
= fopen(filename
, "w");
1856 g_free((gpointer
*)filename
);
1858 fprintf(f
, "%s", new);
1867 if (entry
!= NULL
) {
1876 view_source(const Arg
* arg
) {
1877 gboolean current_mode
= webkit_web_view_get_view_source_mode(client
.gui
.webview
);
1878 webkit_web_view_set_view_source_mode(client
.gui
.webview
, !current_mode
);
1879 webkit_web_view_reload(client
.gui
.webview
);
1883 /* open an external editor defined by the protocol handler for
1884 vimprobableedit on a text box or similar */
1886 open_editor(const Arg
*arg
) {
1890 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1891 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1893 int temp_file_handle
= -1;
1895 /* check if active element is suitable for text editing */
1896 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1897 if (value
== NULL
) {
1901 tag
= g_strdup(value
);
1902 if (strcmp(tag
, "INPUT") == 0) {
1903 /* extra check: type == text */
1904 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1905 if (strcmp(value
, "text") != 0) {
1912 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1917 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1918 text
= g_strdup(value
);
1924 jsapi_evaluate_script("editElem = document.activeElement", &value
, &message
);
1928 /* write text into temporary file */
1929 temp_file_handle
= mkstemp(temp_file_name
);
1930 if (temp_file_handle
== -1) {
1931 message
= g_strdup_printf("Could not create temporary file: %s",
1933 echo_message(Error
, message
);
1939 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1940 message
= g_strdup_printf("Short write to temporary file: %s",
1942 echo_message(Error
, message
);
1948 close(temp_file_handle
);
1952 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1953 success
= open_handler_pid(edit_url
, &child_pid
);
1956 echo_message(Error
, "External editor open failed (no handler for"
1957 " vimprobableedit protocol?)");
1958 unlink(temp_file_name
);
1964 /* mark the active text box as "under processing" */
1965 jsapi_evaluate_script(
1966 "editElem.disabled = true;"
1967 "editElem.originalBackground = "
1968 " editElem.style.background;"
1969 "editElem.style.background = '#aaaaaa';"
1972 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1974 /* temp_file_name is freed in _resume_from_editor */
1981 /* open an external editor defined by the protocol handler for
1982 vimprobableedit on page source */
1984 edit_source(const Arg
*arg
) {
1988 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1989 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1991 int temp_file_handle
= -1;
1993 jsapi_evaluate_script("document.documentElement.innerHTML", &value
, &message
);
1994 text
= g_strdup(value
);
2001 /* write text into temporary file */
2002 temp_file_handle
= mkstemp(temp_file_name
);
2003 if (temp_file_handle
== -1) {
2004 message
= g_strdup_printf("Could not create temporary file: %s",
2006 echo_message(Error
, message
);
2012 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
2013 message
= g_strdup_printf("Short write to temporary file: %s",
2015 echo_message(Error
, message
);
2021 close(temp_file_handle
);
2025 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
2026 success
= open_handler_pid(edit_url
, &child_pid
);
2029 echo_message(Error
, "External editor open failed (no handler for"
2030 " vimprobableedit protocol?)");
2031 unlink(temp_file_name
);
2037 g_child_watch_add(child_pid
, _resume_from_edit_source
, temp_file_name
);
2039 /* temp_file_name is freed in _resume_from_editor */
2047 /* pick up from where open_editor left the work to the glib event loop.
2049 This is called when the external editor exits.
2051 The data argument points to allocated memory containing the temporary file
2054 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
2056 GString
*set_value_js
= g_string_new(
2057 "editElem.value = \"");
2058 g_spawn_close_pid(child_pid
);
2059 gchar
*value
= NULL
, *message
= NULL
;
2060 gchar
*temp_file_name
= data
;
2061 gchar buffer
[BUF_SIZE
] = "";
2062 gchar
*buf_ptr
= buffer
;
2065 jsapi_evaluate_script(
2066 "editElem.disabled = true;"
2067 "editElem.style.background = '#aaaaaa';"
2073 echo_message(Error
, "External editor returned with non-zero status,"
2074 " discarding edits.");
2078 /* re-read the new contents of the file and put it into the HTML element */
2079 if (!access(temp_file_name
, R_OK
) == 0) {
2080 message
= g_strdup_printf("Could not access temporary file: %s",
2084 fp
= fopen(temp_file_name
, "r");
2086 /* this would be too weird to even emit an error message */
2089 jsapi_evaluate_script("editElem.value = '';",
2094 while (EOF
!= (char_read
= fgetc(fp
))) {
2095 if (char_read
== '\n') {
2098 } else if (char_read
== '"') {
2102 *buf_ptr
++ = char_read
;
2104 /* ship out as the buffer when space gets tight. This has
2105 fuzz to save on thinking, plus we have enough space for the
2106 trailing "; in any case. */
2107 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
2109 g_string_append(set_value_js
, buffer
);
2116 g_string_append(set_value_js
, buffer
);
2119 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
2121 /* Fall through, error and normal exit are identical */
2123 jsapi_evaluate_script(
2124 "editElem.disabled = false;"
2125 "editElem.style.background ="
2126 " editElem.originalBackground;"
2129 g_string_free(set_value_js
, TRUE
);
2130 unlink(temp_file_name
);
2131 g_free(temp_file_name
);
2136 /* pick up from where edit_source left the work to the glib event loop.
2138 This is called when the external editor exits.
2140 The data argument points to allocated memory containing the temporary file
2143 _resume_from_edit_source(GPid child_pid
, int child_status
, gpointer data
) {
2145 GString
*set_value_js
= g_string_new(
2146 "document.documentElement.innerHTML = \"");
2147 g_spawn_close_pid(child_pid
);
2148 gchar
*value
= NULL
, *message
= NULL
;
2149 gchar
*temp_file_name
= data
;
2150 gchar buffer
[BUF_SIZE
] = "";
2151 gchar
*buf_ptr
= buffer
;
2155 echo_message(Error
, "External editor returned with non-zero status,"
2156 " discarding edits.");
2160 /* re-read the new contents of the file and put it into the HTML element */
2161 if (!access(temp_file_name
, R_OK
) == 0) {
2162 message
= g_strdup_printf("Could not access temporary file: %s",
2166 fp
= fopen(temp_file_name
, "r");
2168 /* this would be too weird to even emit an error message */
2171 jsapi_evaluate_script("document.documentElement.innerHTML = '';",
2176 while (EOF
!= (char_read
= fgetc(fp
))) {
2177 if (char_read
== '\n') {
2180 } else if (char_read
== '"') {
2184 *buf_ptr
++ = char_read
;
2186 /* ship out as the buffer when space gets tight. This has
2187 fuzz to save on thinking, plus we have enough space for the
2188 trailing "; in any case. */
2189 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
2191 g_string_append(set_value_js
, buffer
);
2198 g_string_append(set_value_js
, buffer
);
2201 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
2205 g_string_free(set_value_js
, TRUE
);
2206 unlink(temp_file_name
);
2207 g_free(temp_file_name
);
2213 focus_input(const Arg
*arg
) {
2216 a
.s
= g_strdup("hints.focusInput();");
2221 client
.state
.manual_focus
= TRUE
;
2229 a
.s
= g_strdup("hints.clearFocus();");
2239 browser_settings(const Arg
*arg
) {
2242 set_error("Missing argument.");
2245 strncpy(line
, arg
->s
, 254);
2246 if (process_set_line(line
))
2249 set_error("Invalid setting.");
2255 search_word(int whichword
) {
2257 static char word
[240];
2258 char *c
= my_pair
.line
;
2260 while (isspace(*c
) && *c
)
2263 switch (whichword
) {
2265 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2270 strncpy(my_pair
.what
, word
, 20);
2273 while (*c
&& k
< 240) {
2278 strncpy(my_pair
.value
, word
, 240);
2286 process_set_line(char *line
) {
2291 WebKitWebSettings
*settings
;
2293 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
2294 my_pair
.line
= line
;
2296 if (!strlen(my_pair
.what
))
2299 while (isspace(*c
) && *c
)
2302 if (*c
== ':' || *c
== '=')
2308 listlen
= LENGTH(browsersettings
);
2309 for (i
= 0; i
< listlen
; i
++) {
2310 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2311 /* mandatory argument not provided */
2312 if (strlen(my_pair
.value
) == 0)
2314 /* process qmark? */
2315 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2316 return (process_save_qmark(my_pair
.value
, client
.gui
.webview
));
2318 /* interpret boolean values */
2319 if (browsersettings
[i
].boolval
) {
2320 if (strncmp(my_pair
.value
, "on", 2) == 0 || strncmp(my_pair
.value
, "true", 4) == 0 || strncmp(my_pair
.value
, "ON", 2) == 0 || strncmp(my_pair
.value
, "TRUE", 4) == 0) {
2322 } else if (strncmp(my_pair
.value
, "off", 3) == 0 || strncmp(my_pair
.value
, "false", 5) == 0 || strncmp(my_pair
.value
, "OFF", 3) == 0 || strncmp(my_pair
.value
, "FALSE", 5) == 0) {
2327 } else if (browsersettings
[i
].colourval
) {
2328 /* interpret as hexadecimal colour */
2329 if (!parse_colour(my_pair
.value
)) {
2332 } else if (browsersettings
[i
].intval
) {
2333 intval
= atoi(my_pair
.value
);
2335 if (browsersettings
[i
].var
!= NULL
) {
2336 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2337 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2338 /* in this case, \0 will not have been copied */
2339 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2340 /* in case this string is also used for a webkit setting, make sure it's consistent */
2341 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2342 echo_message(Info
, "String too long; automatically truncated!");
2345 if (strlen(browsersettings
[i
].webkit
) > 0) {
2346 /* activate appropriate webkit setting */
2347 if (browsersettings
[i
].boolval
) {
2348 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2349 } else if (browsersettings
[i
].intval
) {
2350 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2352 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2354 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2357 if (strlen(my_pair
.what
) == 14) {
2358 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2359 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2360 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2361 complete_case_sensitive
= boolval
;
2363 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2364 toggle_proxy(boolval
);
2365 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2366 toggle_scrollbars(boolval
);
2367 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2368 gtk_widget_set_visible(GTK_WIDGET(client
.gui
.statusbar
), boolval
);
2369 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2370 gtk_widget_set_visible(client
.gui
.inputbox
, boolval
);
2371 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2372 escape_input_on_load
= boolval
;
2373 } else if (strlen(my_pair
.what
) == 7 && strncmp("private", my_pair
.what
, 7) == 0) {
2374 /* Store the state of the last 'on' cookie state before toggling it off */
2376 /* Only update this LastOn state if private mode was false before */
2377 if (!private_mode
) {
2378 CookiePolicyLastOn
= CookiePolicy
;
2380 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
2382 /* If here, we are not in private_mode, so restore the cookie policy */
2383 CookiePolicy
= CookiePolicyLastOn
;
2385 soup_cookie_jar_set_accept_policy(client
.net
.session_cookie_jar
, CookiePolicy
);
2386 private_mode
= boolval
; /* Lastly, set the private_mode for use in other areas */
2387 } else if (strlen(my_pair
.what
) == 7 && strncmp("cookies", my_pair
.what
, 7) == 0) {
2389 if (strncmp(my_pair
.value
, "on", 2) == 0 || strncmp(my_pair
.value
, "true", 4) == 0 ||
2390 strncmp(my_pair
.value
, "ON", 2) == 0 || strncmp(my_pair
.value
, "TRUE", 4) == 0 ||
2391 strncmp(my_pair
.value
, "all", 3) == 0 || strncmp(my_pair
.value
, "ALL", 3) == 0) {
2392 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
2393 } else if (strncmp(my_pair
.value
, "off", 3) == 0 || strncmp(my_pair
.value
, "false", 5) == 0 ||
2394 strncmp(my_pair
.value
, "OFF", 3) == 0 || strncmp(my_pair
.value
, "FALSE", 5) == 0 ||
2395 strncmp(my_pair
.value
, "never", 5) == 0 || strncmp(my_pair
.value
, "NEVER", 5) == 5 ||
2396 strncmp(my_pair
.value
, "none", 4) == 0 || strncmp(my_pair
.value
, "NONE", 4) == 0) {
2397 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
2398 } else if (strncmp(my_pair
.value
, "origin", 6) == 0 || strncmp(my_pair
.value
, "ORIGIN", 6) == 0 ||
2399 strncmp(my_pair
.value
, "no_third", 8) == 0 || strncmp(my_pair
.value
, "NO_THIRD", 8) == 0 ||
2400 strncmp(my_pair
.value
, "no third", 8) == 0 || strncmp(my_pair
.value
, "NO THIRD", 8) == 0) {
2401 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
2405 soup_cookie_jar_set_accept_policy(client
.net
.session_cookie_jar
, CookiePolicy
);
2408 /* SSL certificate checking */
2409 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2412 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", TRUE
, NULL
);
2415 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", FALSE
, NULL
);
2418 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2419 g_object_set(G_OBJECT(client
.net
.session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2421 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2422 set_default_winsize(my_pair
.value
);
2424 if ((strlen(my_pair
.what
) == 2 && strncmp("hi", my_pair
.what
, 2) == 0) ||
2425 (strlen(my_pair
.what
) == 7 && strncmp("history", my_pair
.what
, 7) == 0)) {
2427 set_command_history_len(intval
);
2431 if (browsersettings
[i
].reload
)
2432 webkit_web_view_reload(client
.gui
.webview
);
2440 process_line(char *line
) {
2441 char *c
= line
, *command_hist
;
2443 size_t len
, length
= strlen(line
);
2444 gboolean found
= FALSE
, success
= FALSE
;
2450 /* Ignore blank lines. */
2454 command_hist
= g_strdup(c
);
2456 /* check for colon command aliases first */
2457 for (l
= client
.config
.colon_aliases
; l
; l
= g_list_next(l
)) {
2458 Alias
*alias
= (Alias
*)l
->data
;
2459 if (length
== strlen(alias
->alias
) && strncmp(alias
->alias
, line
, length
) == 0) {
2460 /* reroute to target command */
2462 length
= strlen(alias
->target
);
2467 /* check standard commands */
2468 for (i
= 0; i
< LENGTH(commands
); i
++) {
2469 if (commands
[i
].cmd
== NULL
)
2471 len
= strlen(commands
[i
].cmd
);
2472 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2474 a
.i
= commands
[i
].arg
.i
;
2475 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2476 success
= commands
[i
].func(&a
);
2482 save_command_history(command_hist
);
2483 g_free(command_hist
);
2486 echo_message(Error
, "Not a browser command: %s", c
);
2487 } else if (!success
) {
2488 if (client
.state
.error_msg
!= NULL
) {
2489 echo_message(Error
, client
.state
.error_msg
);
2490 g_free(client
.state
.error_msg
);
2491 client
.state
.error_msg
= NULL
;
2493 echo_message(Error
, "Unknown error. Please file a bug report!");
2500 search_tag(const Arg
* a
) {
2502 const char *filename
;
2503 const char *tag
= a
->s
;
2504 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2508 /* The user must give us something to load up. */
2509 set_error("Bookmark tag required with this option.");
2513 if (strlen(tag
) > MAXTAGSIZE
) {
2514 set_error("Tag too long.");
2518 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2519 f
= fopen(filename
, "r");
2520 g_free((gpointer
*)filename
);
2522 set_error("Couldn't open bookmarks file.");
2525 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2528 while (isspace(s
[t
]))
2530 if (s
[t
] != ']') continue;
2543 foundtag
[i
++] = s
[k
++];
2545 /* foundtag now contains the tag */
2546 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2548 while (isspace(s
[i
])) i
++;
2550 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2552 Arg x
= { .i
= TargetNew
, .s
= url
};
2566 toggle_proxy(gboolean onoff
) {
2568 char *filename
, *new;
2570 if (onoff
== FALSE
) {
2571 g_object_set(client
.net
.session
, "proxy-uri", NULL
, NULL
);
2573 filename
= (char *)g_getenv("http_proxy");
2575 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2578 if (filename
== NULL
)
2579 filename
= (char *)g_getenv("HTTP_PROXY");
2581 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2582 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2583 proxy_uri
= soup_uri_new(new);
2585 g_object_set(client
.net
.session
, "proxy-uri", proxy_uri
, NULL
);
2587 soup_uri_free(proxy_uri
);
2594 toggle_scrollbars(gboolean onoff
) {
2595 Gui
*gui
= &client
.gui
;
2596 if (onoff
== TRUE
) {
2597 gui
->adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2598 gui
->adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2599 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2601 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2602 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2603 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2605 gtk_widget_set_scroll_adjustments (GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2610 void set_default_winsize(const char * const size
) {
2612 int x
= 640, y
= 480;
2614 x
= strtol(size
, &p
, 10);
2615 if (errno
== ERANGE
|| x
<= 0) {
2620 if (p
== size
|| strlen(size
) == p
- size
)
2623 y
= strtol(p
+ 1, NULL
, 10);
2624 if (errno
== ERANGE
|| y
<= 0)
2628 gtk_window_resize(GTK_WINDOW(client
.gui
.window
), x
, y
);
2632 update_url(const char *uri
) {
2633 Gui
*gui
= &client
.gui
;
2634 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2636 WebKitWebFrame
*frame
;
2637 WebKitWebDataSource
*src
;
2638 WebKitNetworkRequest
*request
;
2641 char *sslactivecolor
;
2643 #ifdef ENABLE_HISTORY_INDICATOR
2644 char before
[] = " [";
2646 gboolean back
= webkit_web_view_can_go_back(gui
->webview
);
2647 gboolean fwd
= webkit_web_view_can_go_forward(gui
->webview
);
2650 before
[0] = after
[0] = '\0';
2652 markup
= g_markup_printf_escaped(
2653 #ifdef ENABLE_HISTORY_INDICATOR
2654 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2655 before
, back
? "+" : "", fwd
? "-" : "", after
2657 "<span font=\"%s\">%s</span>", statusfont
, uri
2660 gtk_label_set_markup(GTK_LABEL(gui
->status_url
), markup
);
2663 frame
= webkit_web_view_get_main_frame(gui
->webview
);
2664 src
= webkit_web_frame_get_data_source(frame
);
2665 request
= webkit_web_data_source_get_request(src
);
2666 msg
= webkit_network_request_get_message(request
);
2667 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2669 sslactivecolor
= sslbgcolor
;
2671 sslactivecolor
= sslinvalidbgcolor
;
2673 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2674 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &color
);
2675 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2676 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_url
), GTK_STATE_NORMAL
, &color
);
2677 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_state
), GTK_STATE_NORMAL
, &color
);
2682 State
* state
= &client
.state
;
2684 int download_count
= g_list_length(state
->activeDownloads
);
2685 GString
*status
= g_string_new("");
2687 /* construct the status line */
2689 /* count, modkey and input buffer */
2690 g_string_append_printf(status
, "%.0d", state
->count
);
2691 if (state
->current_modkey
) g_string_append_c(status
, state
->current_modkey
);
2693 /* the number of active downloads */
2694 if (state
->activeDownloads
) {
2695 g_string_append_printf(status
, " %d active %s", download_count
,
2696 (download_count
== 1) ? "download" : "downloads");
2699 #ifdef ENABLE_WGET_PROGRESS_BAR
2700 /* the progressbar */
2703 char progressbar
[progressbartick
+ 1];
2705 if (state
->activeDownloads
) {
2709 for (ptr
= state
->activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2710 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2713 progress
/= download_count
;
2715 } else if (webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FINISHED
2716 && webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FAILED
) {
2718 progress
= webkit_web_view_get_progress(client
.gui
.webview
) * 100;
2721 if (progress
>= 0) {
2722 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2723 g_string_append_printf(status
, " %c%s%c",
2724 progressborderleft
, progressbar
, progressborderright
);
2729 /* and the current scroll position */
2731 int max
= gtk_adjustment_get_upper(client
.gui
.adjust_v
) - gtk_adjustment_get_page_size(client
.gui
.adjust_v
);
2732 int val
= (int)(gtk_adjustment_get_value(client
.gui
.adjust_v
) / max
* 100);
2735 g_string_append(status
, " All");
2737 g_string_append(status
, " Top");
2738 else if (val
== 100)
2739 g_string_append(status
, " Bot");
2741 g_string_append_printf(status
, " %d%%", val
);
2745 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2746 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_state
), markup
);
2749 g_string_free(status
, TRUE
);
2753 setup_client(void) {
2754 State
*state
= &client
.state
;
2755 Config
*config
= &client
.config
;
2757 state
->mode
= ModeNormal
;
2759 state
->rememberedURI
[0] = '\0';
2760 state
->manual_focus
= FALSE
;
2761 state
->is_inspecting
= FALSE
;
2762 state
->commandhistory
= NULL
;
2763 state
->commandpointer
= 0;
2765 config
->colon_aliases
= NULL
;
2766 config
->cookie_timeout
= 4800;
2772 client
.config
.modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2773 char *ptr
= client
.config
.modkeys
;
2775 for (i
= 0; i
< LENGTH(keys
); i
++)
2776 if (keys
[i
].modkey
&& !strchr(client
.config
.modkeys
, keys
[i
].modkey
))
2777 *(ptr
++) = keys
[i
].modkey
;
2778 client
.config
.modkeys
= realloc(client
.config
.modkeys
, &ptr
[0] - &client
.config
.modkeys
[0] + 1);
2783 Gui
*gui
= &client
.gui
;
2784 State
*state
= &client
.state
;
2786 gui
->scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2787 gui
->scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2788 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2789 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2790 if (client
.state
.embed
) {
2791 gui
->window
= GTK_WINDOW(gtk_plug_new(client
.state
.embed
));
2793 gui
->window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2794 gtk_window_set_wmclass(GTK_WINDOW(gui
->window
), "vimprobable2", "Vimprobable2");
2796 gtk_window_set_default_size(GTK_WINDOW(gui
->window
), 640, 480);
2797 gui
->box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2798 gui
->inputbox
= gtk_entry_new();
2799 gui
->webview
= (WebKitWebView
*)webkit_web_view_new();
2800 gui
->statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2801 gui
->eventbox
= gtk_event_box_new();
2802 gui
->status_url
= gtk_label_new(NULL
);
2803 gui
->status_state
= gtk_label_new(NULL
);
2805 PangoFontDescription
*font
;
2806 GdkGeometry hints
= { 1, 1 };
2807 gui
->inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(gui
->webview
));
2809 state
->clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2810 state
->clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2812 gdk_color_parse(statusbgcolor
, &bg
);
2813 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &bg
);
2814 gtk_widget_set_name(GTK_WIDGET(gui
->window
), "Vimprobable2");
2815 gtk_window_set_geometry_hints(gui
->window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2817 state
->keymap
= gdk_keymap_get_default();
2819 #ifdef DISABLE_SCROLLBAR
2820 gui
->viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2821 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2823 /* Ensure we still see scrollbars. */
2824 gui
->viewport
= gtk_scrolled_window_new(gui
->adjust_h
, gui
->adjust_v
);
2827 gui
->pane
= gtk_vpaned_new();
2828 gtk_paned_pack1(GTK_PANED(gui
->pane
), GTK_WIDGET(gui
->box
), TRUE
, TRUE
);
2831 gtk_container_add(GTK_CONTAINER(gui
->viewport
), GTK_WIDGET(gui
->webview
));
2833 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2834 * titlebars, we can still scroll.
2836 gtk_widget_set_scroll_adjustments(GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2838 font
= pango_font_description_from_string(urlboxfont
[0]);
2839 gtk_widget_modify_font(GTK_WIDGET(gui
->inputbox
), font
);
2840 pango_font_description_free(font
);
2841 gtk_entry_set_inner_border(GTK_ENTRY(gui
->inputbox
), NULL
);
2842 gtk_misc_set_alignment(GTK_MISC(gui
->status_url
), 0.0, 0.0);
2843 gtk_misc_set_alignment(GTK_MISC(gui
->status_state
), 1.0, 0.0);
2844 gtk_box_pack_start(gui
->statusbar
, gui
->status_url
, TRUE
, TRUE
, 2);
2845 gtk_box_pack_start(gui
->statusbar
, gui
->status_state
, FALSE
, FALSE
, 2);
2846 gtk_container_add(GTK_CONTAINER(gui
->eventbox
), GTK_WIDGET(gui
->statusbar
));
2847 gtk_box_pack_start(gui
->box
, gui
->viewport
, TRUE
, TRUE
, 0);
2848 gtk_box_pack_start(gui
->box
, gui
->eventbox
, FALSE
, FALSE
, 0);
2849 gtk_entry_set_has_frame(GTK_ENTRY(gui
->inputbox
), FALSE
);
2850 gtk_box_pack_end(gui
->box
, gui
->inputbox
, FALSE
, FALSE
, 0);
2851 gtk_container_add(GTK_CONTAINER(gui
->window
), GTK_WIDGET(gui
->pane
));
2852 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
2853 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
2854 set_widget_font_and_color(gui
->inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2855 g_object_set(gtk_widget_get_settings(gui
->inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2860 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2861 char *filename
, *file_url
;
2863 client
.net
.session
= webkit_get_default_session();
2864 g_object_set(G_OBJECT(client
.net
.session
), "ssl-ca-file", ca_bundle
, NULL
);
2865 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", strict_ssl
, NULL
);
2866 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2867 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2868 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2869 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2870 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2871 g_object_set(G_OBJECT(settings
), "enable-html5-local-storage", enableLocalstorage
, NULL
);
2872 g_object_set(G_OBJECT(settings
), "enable-html5-database", enableDatabase
, NULL
);
2873 g_object_set(G_OBJECT(settings
), "javascript-can-open-windows-automatically", javascriptPopups
, NULL
);
2874 filename
= g_strdup_printf(USER_STYLESHEET
);
2875 file_url
= g_strdup_printf("file://%s", filename
);
2876 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2879 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2880 g_object_get(G_OBJECT(settings
), "zoom-step", &client
.config
.zoomstep
, NULL
);
2881 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2884 toggle_proxy(use_proxy
);
2889 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
2890 #ifdef ENABLE_COOKIE_SUPPORT
2892 g_signal_connect_after(G_OBJECT(client
.net
.session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2894 /* Accept-language header */
2895 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2897 g_object_connect(G_OBJECT(client
.gui
.window
),
2898 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2901 g_signal_connect(G_OBJECT(frame
),
2902 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2904 g_object_connect(G_OBJECT(client
.gui
.webview
),
2905 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2906 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2907 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2908 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2909 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2910 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2911 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2912 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2913 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2914 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2915 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2916 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2917 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2919 /* webview adjustment */
2920 g_object_connect(G_OBJECT(client
.gui
.adjust_v
),
2921 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2924 g_object_connect(G_OBJECT(client
.gui
.inputbox
),
2925 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2926 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2927 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2928 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2931 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2932 "inspect-web-view", G_CALLBACK(inspector_new_cb
), NULL
);
2933 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2934 "show-window", G_CALLBACK(inspector_show_cb
), NULL
);
2935 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2936 "close-window", G_CALLBACK(inspector_close_cb
), NULL
);
2937 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2938 "finished", G_CALLBACK(inspector_finished_cb
), NULL
);
2941 #ifdef ENABLE_USER_SCRIPTFILE
2943 scripts_run_user_file() {
2944 gchar
*js
= NULL
, *user_scriptfile
= NULL
;
2945 GError
*error
= NULL
;
2947 user_scriptfile
= g_strdup_printf(USER_SCRIPTFILE
);
2949 /* run the users script file */
2950 if (g_file_test(user_scriptfile
, G_FILE_TEST_IS_REGULAR
)
2951 && g_file_get_contents(user_scriptfile
, &js
, NULL
, &error
)) {
2953 gchar
*value
= NULL
, *message
= NULL
;
2955 jsapi_evaluate_script(js
, &value
, &message
);
2958 fprintf(stderr
, "%s", message
);
2963 fprintf(stderr
, "Cannot open %s: %s\n", user_scriptfile
, error
? error
->message
: "file not found");
2966 g_free(user_scriptfile
);
2970 #ifdef ENABLE_COOKIE_SUPPORT
2974 Network
*net
= &client
.net
;
2975 if (net
->file_cookie_jar
)
2976 g_object_unref(net
->file_cookie_jar
);
2978 if (net
->session_cookie_jar
)
2979 g_object_unref(net
->session_cookie_jar
);
2981 net
->session_cookie_jar
= soup_cookie_jar_new();
2982 soup_cookie_jar_set_accept_policy(net
->session_cookie_jar
, CookiePolicy
);
2984 net
->cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2988 g_signal_connect(G_OBJECT(net
->file_cookie_jar
), "changed",
2989 G_CALLBACK(update_cookie_jar
), NULL
);
2992 /* This function could be used for any header requests we receive
2993 * for not, it's limited to handling cookies
2996 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2998 SoupMessageHeaders
*soup_msg_h
;
3002 soup_msg_h
= soup_msg
->request_headers
;
3003 soup_message_headers_remove(soup_msg_h
, "Cookie");
3004 uri
= soup_message_get_uri(soup_msg
);
3005 soup_message_set_first_party(soup_msg
, uri
);
3006 if ((cookie_str
= get_cookies(uri
))) {
3007 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
3011 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_response_headers
), NULL
);
3017 get_cookies(SoupURI
*soup_uri
) {
3020 cookie_str
= soup_cookie_jar_get_cookies(client
.net
.file_cookie_jar
, soup_uri
, TRUE
);
3026 handle_response_headers(SoupMessage
*soup_msg
, gpointer unused
)
3028 GSList
*resp_cookie
= NULL
, *cookie_list
;
3030 SoupURI
*uri
= soup_message_get_uri(soup_msg
);
3032 client
.net
.http_status
= soup_msg
->status_code
;
3033 if (CookiePolicy
!= SOUP_COOKIE_JAR_ACCEPT_NEVER
) {
3034 cookie_list
= soup_cookies_from_response(soup_msg
);
3035 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
3037 SoupDate
*soup_date
;
3038 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
3040 if (client
.config
.cookie_timeout
&& cookie
->expires
== NULL
) {
3041 soup_date
= soup_date_new_from_time_t(time(NULL
) + client
.config
.cookie_timeout
* 10);
3042 soup_cookie_set_expires(cookie
, soup_date
);
3043 soup_date_free(soup_date
);
3045 if (CookiePolicy
!= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
) {
3046 /* no third party cookies: for libsoup 2.4 and later, the following should work */
3047 /*soup_cookie_jar_add_cookie_with_first_party(client.net.file_cookie_jar, uri, cookie);*/
3048 if (strcmp(soup_uri_get_host(uri
), soup_cookie_get_domain(cookie
)) == 0) {
3049 soup_cookie_jar_add_cookie(client
.net
.file_cookie_jar
, cookie
);
3052 soup_cookie_jar_add_cookie(client
.net
.file_cookie_jar
, cookie
);
3056 soup_cookies_free(cookie_list
);
3063 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
3066 /* Nothing to do. */
3070 if (CookiePolicy
!= SOUP_COOKIE_JAR_ACCEPT_NEVER
) {
3072 copy
= soup_cookie_copy(new);
3074 soup_cookie_jar_add_cookie(client
.net
.session_cookie_jar
, copy
);
3081 load_all_cookies(void)
3083 Network
*net
= &client
.net
;
3084 GSList
*cookie_list
;
3085 net
->file_cookie_jar
= soup_cookie_jar_text_new(net
->cookie_store
, COOKIES_STORAGE_READONLY
);
3087 /* Put them back in the session store. */
3088 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(net
->file_cookie_jar
);
3089 cookie_list
= cookies_from_file
;
3091 for (; cookies_from_file
;
3092 cookies_from_file
= cookies_from_file
->next
)
3094 soup_cookie_jar_add_cookie(net
->session_cookie_jar
, cookies_from_file
->data
);
3097 soup_cookies_free(cookies_from_file
);
3098 g_slist_free(cookie_list
);
3107 /* Free up any nasty globals before exiting. */
3108 #ifdef ENABLE_COOKIE_SUPPORT
3109 if (client
.net
.cookie_store
)
3110 g_free(client
.net
.cookie_store
);
3116 main(int argc
, char *argv
[]) {
3118 static char url
[256] = "";
3119 static gboolean ver
= false;
3120 static gboolean configfile_exists
= FALSE
;
3121 static const char *cfile
= NULL
;
3122 static gchar
*winid
= NULL
;
3123 static GOptionEntry opts
[] = {
3124 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
3125 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
3126 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
3131 Config
*config
= &client
.config
;
3133 /* command line argument: version */
3134 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
3135 g_printerr("can't init gtk: %s\n", err
->message
);
3137 return EXIT_FAILURE
;
3141 printf("%s\n", INTERNAL_VERSION
);
3142 return EXIT_SUCCESS
;
3147 if (getenv("TMPDIR")) {
3148 strncpy(temp_dir
, getenv("TMPDIR"), MAX_SETTING_SIZE
);
3149 temp_dir
[MAX_SETTING_SIZE
-1] = 0;
3152 if( getenv("XDG_CONFIG_HOME") )
3153 config
->config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
3155 config
->config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
3157 sprintf(downloads_path
, "%s", getenv("HOME"));
3160 config
->configfile
= g_strdup(cfile
);
3162 config
->configfile
= g_strdup_printf(RCFILE
);
3164 #if !GLIB_CHECK_VERSION(2, 35, 0)
3165 if (!g_thread_supported()) {
3166 # if !GLIB_CHECK_VERSION(2, 32, 0)
3167 g_thread_init(NULL
);
3173 if (strncmp(winid
, "0x", 2) == 0) {
3174 client
.state
.embed
= strtol(winid
, NULL
, 16);
3176 client
.state
.embed
= atoi(winid
);
3183 #ifdef ENABLE_COOKIE_SUPPORT
3187 make_searchengines_list(searchengines
, LENGTH(searchengines
));
3188 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
3190 /* Check if the specified file exists. */
3191 /* And only warn the user, if they explicitly asked for a config on the
3194 if (!(access(config
->configfile
, F_OK
) == 0) && cfile
) {
3195 echo_message(Info
, "Config file '%s' doesn't exist", cfile
);
3196 } else if ((access(config
->configfile
, F_OK
) == 0))
3197 configfile_exists
= true;
3199 /* read config file */
3200 /* But only report errors if we failed, and the file existed. */
3201 if ((SUCCESS
!= read_rcfile(config
->configfile
)) && configfile_exists
) {
3202 echo_message(Error
, "Error in config file '%s'", config
->configfile
);
3203 g_free(config
->configfile
);
3206 /* command line argument: URL */
3208 strncpy(url
, argv
[argc
- 1], 255);
3210 strncpy(url
, startpage
, 255);
3213 a
.i
= TargetCurrent
;
3220 return EXIT_SUCCESS
;