2 (c) 2009 by Leon Winter
3 (c) 2009-2013 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-2013 by Daniel Carl
9 (c) 2012 by Matthew Carter
15 #include <sys/types.h>
20 #include "vimprobable.h"
21 #include "utilities.h"
22 #include "callbacks.h"
23 #include "javascript.h"
25 /* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
26 #define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
27 #define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
29 /* remove unused bits, numlock symbol and buttons from keymask */
30 #define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
32 #define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
33 #define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
34 (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
37 static void inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
);
38 static gboolean
inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
);
39 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
);
40 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
);
41 static WebKitWebView
* inspector_new_cb(WebKitWebInspector
* inspector
, WebKitWebView
* web_view
);
42 static gboolean
inspector_show_cb(WebKitWebInspector
*inspector
);
43 static gboolean
inspector_close_cb(WebKitWebInspector
*inspector
);
44 static void inspector_finished_cb(WebKitWebInspector
*inspector
);
45 static gboolean
notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
);
46 static gboolean
webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
);
47 static gboolean
webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
);
48 static void webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
);
49 static gboolean
webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
);
50 static void webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
51 static void webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
52 static gboolean
webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
53 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
54 static void webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
);
55 static gboolean
webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
56 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
);
57 static WebKitWebView
* webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
);
58 static void webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
);
59 static void webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
);
60 static void window_destroyed_cb(GtkWidget
*window
, gpointer func_data
);
61 static gboolean
blank_cb(void);
64 static gboolean
bookmark(const Arg
*arg
);
65 static gboolean
browser_settings(const Arg
*arg
);
66 static gboolean
commandhistoryfetch(const Arg
*arg
);
67 static gboolean
complete(const Arg
*arg
);
68 static gboolean
descend(const Arg
*arg
);
69 gboolean
echo(const Arg
*arg
);
70 static gboolean
focus_input(const Arg
*arg
);
71 static gboolean
open_editor(const Arg
*arg
);
72 void _resume_from_editor(GPid child_pid
, int status
, gpointer data
);
73 static gboolean
input(const Arg
*arg
);
74 static gboolean
open_inspector(const Arg
* arg
);
75 static gboolean
navigate(const Arg
*arg
);
76 static gboolean
number(const Arg
*arg
);
77 static gboolean
open_arg(const Arg
*arg
);
78 static gboolean
open_remembered(const Arg
*arg
);
79 static gboolean
paste(const Arg
*arg
);
80 static gboolean
quickmark(const Arg
*arg
);
81 static gboolean
quit(const Arg
*arg
);
82 static gboolean
revive(const Arg
*arg
);
83 static gboolean
print_frame(const Arg
*arg
);
84 static gboolean
search(const Arg
*arg
);
85 static gboolean
set(const Arg
*arg
);
86 static gboolean
script(const Arg
*arg
);
87 static gboolean
scroll(const Arg
*arg
);
88 static gboolean
search_tag(const Arg
*arg
);
89 static gboolean
yank(const Arg
*arg
);
90 static gboolean
view_source(const Arg
* arg
);
91 static gboolean
zoom(const Arg
*arg
);
92 static gboolean
fake_key_event(const Arg
*arg
);
94 static void clear_focus(void);
95 static void update_url(const char *uri
);
96 static void setup_client(void);
97 static void setup_modkeys(void);
98 static void setup_gui(void);
99 static void setup_settings(void);
100 static void setup_signals(void);
101 static void ascii_bar(int total
, int state
, char *string
);
102 static gchar
*jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
);
103 static void jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
);
104 static void download_progress(WebKitDownload
*d
, GParamSpec
*pspec
);
105 static void set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
,
106 const char *bg_color_str
, const char *fg_color_str
);
107 static void scripts_run_user_file(void);
108 static void show_link(const char *link
);
110 static gboolean
history(void);
111 static gboolean
process_set_line(char *line
);
112 void save_command_history(char *line
);
113 void toggle_proxy(gboolean onoff
);
114 void toggle_scrollbars(gboolean onoff
);
115 void set_default_winsize(const char * const size
);
117 gboolean
process_keypress(GdkEventKey
*event
);
118 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
);
119 GtkWidget
* fill_eventbox(const char * completion_line
);
120 static void mop_up(void);
130 /* Cookie support. */
131 #ifdef ENABLE_COOKIE_SUPPORT
132 static void setup_cookies(void);
133 static char *get_cookies(SoupURI
*soup_uri
);
134 static void load_all_cookies(void);
135 static void new_generic_request(SoupSession
*soup_ses
, SoupMessage
*soup_msg
, gpointer unused
);
136 static void update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new);
137 static void handle_response_headers(SoupMessage
*soup_msg
, gpointer unused
);
144 window_destroyed_cb(GtkWidget
*window
, gpointer func_data
) {
149 webview_title_changed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, char *title
, gpointer user_data
) {
150 gtk_window_set_title(client
.gui
.window
, title
);
154 webview_progress_changed_cb(WebKitWebView
*webview
, int progress
, gpointer user_data
) {
155 #ifdef ENABLE_GTK_PROGRESS_BAR
156 gtk_entry_set_progress_fraction(GTK_ENTRY(client
.gui
.inputbox
), progress
== 100 ? 0 : (double)progress
/100);
161 #ifdef ENABLE_WGET_PROGRESS_BAR
163 ascii_bar(int total
, int state
, char *string
) {
166 for (i
= 0; i
< state
; i
++)
167 string
[i
] = progressbartickchar
;
168 string
[i
++] = progressbarcurrent
;
169 for (; i
< total
; i
++)
170 string
[i
] = progressbarspacer
;
176 webview_load_committed_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
177 Arg a
= { .i
= Silent
, .s
= g_strdup(JS_SETUP_HINTS
) };
178 const char *uri
= webkit_web_view_get_uri(webview
);
183 scripts_run_user_file();
185 if (client
.state
.mode
== ModeInsert
|| client
.state
.mode
== ModeHints
) {
186 Arg a
= { .i
= ModeNormal
};
189 client
.state
.manual_focus
= FALSE
;
193 webview_load_finished_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
194 WebKitWebSettings
*settings
= webkit_web_view_get_settings(webview
);
197 g_object_get(settings
, "enable-scripts", &scripts
, NULL
);
198 if (escape_input_on_load
&& scripts
&& !client
.state
.manual_focus
&& !gtk_widget_is_focus(client
.gui
.inputbox
)) {
201 if (HISTORY_MAX_ENTRIES
> 0)
207 webview_open_js_window_cb(WebKitWebView
* temp_view
, GParamSpec param_spec
) {
208 /* retrieve the URI of the temporary webview */
209 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_web_view_get_uri(temp_view
) };
211 webkit_web_view_stop_loading(temp_view
);
212 gtk_widget_destroy(GTK_WIDGET(temp_view
));
213 /* open the requested window */
217 static WebKitWebView
*
218 webview_open_in_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, gpointer user_data
) {
219 /* create a temporary webview to execute the script in */
220 WebKitWebView
*temp_view
= WEBKIT_WEB_VIEW(webkit_web_view_new());
221 /* wait until the new webview receives its new URI */
222 g_object_connect(temp_view
, "signal::notify::uri", G_CALLBACK(webview_open_js_window_cb
), NULL
, NULL
);
227 webview_new_window_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
228 WebKitWebNavigationAction
*action
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
229 Arg a
= { .i
= TargetNew
, .s
= (char*)webkit_network_request_get_uri(request
) };
231 webkit_web_policy_decision_ignore(decision
);
236 webview_mimetype_cb(WebKitWebView
*webview
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
,
237 char *mime_type
, WebKitWebPolicyDecision
*decision
, gpointer user_data
) {
238 SoupMessage
*msg
= webkit_network_request_get_message(request
);
239 guint http_status
= msg
->status_code
;
241 if (webkit_web_view_can_show_mime_type(webview
, mime_type
) == FALSE
) {
242 if (SOUP_STATUS_IS_SUCCESSFUL(http_status
)) {
243 webkit_web_policy_decision_download(decision
);
252 static WebKitWebView
*
253 inspector_new_cb(WebKitWebInspector
*inspector
, WebKitWebView
* web_view
) {
254 return WEBKIT_WEB_VIEW(webkit_web_view_new());
258 inspector_show_cb(WebKitWebInspector
*inspector
) {
259 WebKitWebView
*webview
;
260 State
*state
= &client
.state
;
262 if (state
->is_inspecting
) {
266 webview
= webkit_web_inspector_get_web_view(inspector
);
267 gtk_paned_pack2(GTK_PANED(client
.gui
.pane
), GTK_WIDGET(webview
), TRUE
, TRUE
);
268 gtk_widget_show(GTK_WIDGET(webview
));
270 state
->is_inspecting
= TRUE
;
276 inspector_close_cb(WebKitWebInspector
*inspector
) {
278 State
*state
= &client
.state
;
280 if (!state
->is_inspecting
) {
283 widget
= GTK_WIDGET(webkit_web_inspector_get_web_view(inspector
));
284 gtk_widget_hide(widget
);
285 gtk_widget_destroy(widget
);
287 state
->is_inspecting
= FALSE
;
293 inspector_finished_cb(WebKitWebInspector
*inspector
) {
298 webview_download_cb(WebKitWebView
*webview
, WebKitDownload
*download
, gpointer user_data
) {
299 const gchar
*filename
;
302 WebKitDownloadStatus status
;
304 filename
= webkit_download_get_suggested_filename(download
);
305 if (filename
== NULL
|| strlen(filename
) == 0) {
306 filename
= "vimprobable_download";
308 path
= g_build_filename(g_strdup_printf(downloads_path
), filename
, NULL
);
309 uri
= g_strconcat("file://", path
, NULL
);
310 webkit_download_set_destination_uri(download
, uri
);
313 size
= (uint32_t)webkit_download_get_total_size(download
);
315 echo_message(Info
, "Download %s started (expected size: %u bytes)...", filename
, size
);
317 echo_message(Info
, "Download %s started (unknown size)...", filename
);
318 client
.state
.activeDownloads
= g_list_prepend(client
.state
.activeDownloads
, download
);
319 g_signal_connect(download
, "notify::progress", G_CALLBACK(download_progress
), NULL
);
320 g_signal_connect(download
, "notify::status", G_CALLBACK(download_progress
), NULL
);
321 status
= webkit_download_get_status(download
);
322 if (status
== WEBKIT_DOWNLOAD_STATUS_CREATED
)
323 webkit_download_start(download
);
334 download_progress(WebKitDownload
*d
, GParamSpec
*pspec
) {
335 WebKitDownloadStatus status
= webkit_download_get_status(d
);
337 if (status
!= WEBKIT_DOWNLOAD_STATUS_STARTED
&& status
!= WEBKIT_DOWNLOAD_STATUS_CREATED
) {
338 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
) {
339 echo_message(Error
, "Error while downloading %s", webkit_download_get_suggested_filename(d
));
341 echo_message(Info
, "Download %s finished", webkit_download_get_suggested_filename(d
));
343 client
.state
.activeDownloads
= g_list_remove(client
.state
.activeDownloads
, d
);
350 process_keypress(GdkEventKey
*event
) {
351 State
*state
= &client
.state
;
354 GdkModifierType irrelevant
;
356 /* Get a mask of modifiers that shouldn't be considered for this event.
357 * E.g.: It shouldn't matter whether ';' is shifted or not. */
358 gdk_keymap_translate_keyboard_state(state
->keymap
, event
->hardware_keycode
,
359 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
361 current
= client
.config
.keylistroot
;
363 while (current
!= NULL
) {
364 if (current
->Element
.mask
== (CLEAN(event
->state
) & ~irrelevant
)
365 && (current
->Element
.modkey
== state
->current_modkey
366 || (!current
->Element
.modkey
&& !state
->current_modkey
)
367 || current
->Element
.modkey
== GDK_VoidSymbol
) /* wildcard */
368 && current
->Element
.key
== keyval
369 && current
->Element
.func
)
370 if (current
->Element
.func(¤t
->Element
.arg
)) {
371 state
->current_modkey
= state
->count
= 0;
375 current
= current
->next
;
381 webview_keypress_cb(WebKitWebView
*webview
, GdkEventKey
*event
) {
382 State
*state
= &client
.state
;
383 Arg a
= { .i
= ModeNormal
, .s
= NULL
};
385 GdkModifierType irrelevant
;
387 /* Get a mask of modifiers that shouldn't be considered for this event.
388 * E.g.: It shouldn't matter whether ';' is shifted or not. */
389 gdk_keymap_translate_keyboard_state(state
->keymap
, event
->hardware_keycode
,
390 event
->state
, event
->group
, &keyval
, NULL
, NULL
, &irrelevant
);
392 switch (state
->mode
) {
394 if ((CLEAN(event
->state
) & ~irrelevant
) == 0) {
395 if (IS_ESCAPE(event
)) {
396 echo_message(Info
, "");
398 } else if (state
->current_modkey
== 0 && ((event
->keyval
>= GDK_1
&& event
->keyval
<= GDK_9
)
399 || (event
->keyval
== GDK_0
&& state
->count
))) {
400 state
->count
= (state
->count
? state
->count
* 10 : 0) + (event
->keyval
- GDK_0
);
403 } else if (strchr(client
.config
.modkeys
, event
->keyval
) && state
->current_modkey
!= event
->keyval
) {
404 state
->current_modkey
= event
->keyval
;
410 if (process_keypress(event
) == TRUE
) return TRUE
;
414 if (IS_ESCAPE(event
)) {
416 a
.s
= g_strdup("hints.clearFocus();");
421 } else if (CLEAN(event
->state
) & GDK_CONTROL_MASK
) {
422 /* keybindings of non-printable characters */
423 if (process_keypress(event
) == TRUE
) return TRUE
;
425 case ModePassThrough
:
426 if (IS_ESCAPE(event
)) {
427 echo_message(Info
, "");
433 echo_message(Info
, "");
441 set_widget_font_and_color(GtkWidget
*widget
, const char *font_str
, const char *bg_color_str
,
442 const char *fg_color_str
) {
445 PangoFontDescription
*font
;
447 font
= pango_font_description_from_string(font_str
);
448 gtk_widget_modify_font(widget
, font
);
449 pango_font_description_free(font
);
452 gdk_color_parse(fg_color_str
, &fg_color
);
454 gdk_color_parse(bg_color_str
, &bg_color
);
456 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, fg_color_str
? &fg_color
: NULL
);
457 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, bg_color_str
? &bg_color
: NULL
);
463 show_link(const char *link
) {
466 markup
= g_markup_printf_escaped("<span font=\"%s\">Link: %s</span>", statusfont
, link
);
467 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_url
), markup
);
468 strncpy(client
.state
.rememberedURI
, link
, BUF_SIZE
);
473 webview_hoverlink_cb(WebKitWebView
*webview
, char *title
, char *link
, gpointer data
) {
474 const char *uri
= webkit_web_view_get_uri(webview
);
476 memset(client
.state
.rememberedURI
, 0, BUF_SIZE
);
484 webview_console_cb(WebKitWebView
*webview
, char *message
, int line
, char *source
, gpointer user_data
) {
487 /* Don't change internal mode if the browser doesn't have focus to prevent inconsistent states */
488 if (gtk_window_has_toplevel_focus(client
.gui
.window
)) {
489 if (!strcmp(message
, "hintmode_off") || !strcmp(message
, "insertmode_off")) {
492 } else if (!strcmp(message
, "insertmode_on")) {
501 inputbox_activate_cb(GtkEntry
*entry
, gpointer user_data
) {
502 Gui
*gui
= &client
.gui
;
503 State
*state
= &client
.state
;
505 guint16 length
= gtk_entry_get_text_length(entry
);
507 gboolean forward
= FALSE
;
509 a
.i
= HideCompletion
;
513 text
= (char*)gtk_entry_get_text(entry
);
515 /* move focus from inputbox to print potential messages that could not be
516 * printed as long as the inputbox is focused */
517 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
519 if (length
> 1 && text
[0] == ':') {
520 process_line((text
+ 1));
521 } else if (length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
522 webkit_web_view_unmark_text_matches(gui
->webview
);
523 #ifdef ENABLE_MATCH_HIGHLITING
524 webkit_web_view_mark_text_matches(gui
->webview
, &text
[1], FALSE
, 0);
525 webkit_web_view_set_highlight_text_matches(gui
->webview
, TRUE
);
528 #ifndef ENABLE_INCREMENTAL_SEARCH
530 a
.i
= searchoptions
| (forward
? DirectionForward
: DirectionBackwards
);
533 state
->search_direction
= forward
;
534 if (state
->search_handle
) {
535 g_free(state
->search_handle
);
537 state
->search_handle
= g_strdup(&text
[1]);
539 } else if (text
[0] == '.' || text
[0] == ',' || text
[0] == ';') {
541 a
.s
= g_strdup_printf("hints.fire();");
547 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
551 inputbox_keypress_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
554 State
*state
= &client
.state
;
556 if (state
->mode
== ModeHints
) {
557 if (event
->keyval
== GDK_Tab
) {
559 a
.s
= g_strdup_printf("hints.focusNextHint();");
565 if (event
->keyval
== GDK_ISO_Left_Tab
) {
567 a
.s
= g_strdup_printf("hints.focusPreviousHint();");
573 if (event
->keyval
== GDK_Return
) {
575 a
.s
= g_strdup_printf("hints.fire();");
582 switch (event
->keyval
) {
583 case GDK_bracketleft
:
585 if (!IS_ESCAPE(event
)) break;
586 a
.i
= HideCompletion
;
589 state
->commandpointer
= 0;
598 return commandhistoryfetch(&a
);
602 return commandhistoryfetch(&a
);
604 case GDK_ISO_Left_Tab
:
610 if (state
->mode
== ModeHints
) {
611 if ((CLEAN(event
->state
) & GDK_SHIFT_MASK
) &&
612 (CLEAN(event
->state
) & GDK_CONTROL_MASK
) &&
613 (event
->keyval
== GDK_BackSpace
)) {
616 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
623 numval
= g_unichar_digit_value((gunichar
) gdk_keyval_to_unicode(event
->keyval
));
624 if ((numval
>= 1 && numval
<= 9) || (numval
== 0 && state
->count
)) {
625 /* allow a zero as non-first number */
626 state
->count
= (state
->count
? state
->count
* 10 : 0) + numval
;
628 a
.s
= g_strdup_printf("hints.updateHints(%d);", state
->count
);
640 notify_event_cb(GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
) {
642 WebKitHitTestResult
*result
;
643 WebKitHitTestResultContext context
;
644 State
*state
= &client
.state
;
645 if (state
->mode
== ModeNormal
&& event
->type
== GDK_BUTTON_RELEASE
) {
646 /* handle mouse click events */
647 for (i
= 0; i
< LENGTH(mouse
); i
++) {
648 if (mouse
[i
].mask
== CLEAN(event
->button
.state
)
649 && (mouse
[i
].modkey
== state
->current_modkey
650 || (!mouse
[i
].modkey
&& !state
->current_modkey
)
651 || mouse
[i
].modkey
== GDK_VoidSymbol
) /* wildcard */
652 && mouse
[i
].button
== event
->button
.button
654 if (mouse
[i
].func(&mouse
[i
].arg
)) {
655 state
->current_modkey
= state
->count
= 0;
661 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
662 g_object_get(result
, "context", &context
, NULL
);
663 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
) {
664 Arg a
= { .i
= ModeInsert
};
666 state
->manual_focus
= TRUE
;
668 } else if (state
->mode
== ModeInsert
&& event
->type
== GDK_BUTTON_RELEASE
) {
669 result
= webkit_web_view_get_hit_test_result(WEBKIT_WEB_VIEW(widget
), (GdkEventButton
*)event
);
670 g_object_get(result
, "context", &context
, NULL
);
671 if (!(context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)) {
672 Arg a
= { .i
= ModeNormal
};
676 gchar
*value
= NULL
, *message
= NULL
;
677 jsapi_evaluate_script("window.getSelection().focusNode", &value
, &message
);
678 if (value
&& !strcmp(value
, "[object HTMLFormElement]")) {
679 Arg a
= { .i
= ModeInsert
, .s
= NULL
};
681 state
->manual_focus
= TRUE
;
689 static gboolean
inputbox_keyrelease_cb(GtkEntry
*entry
, GdkEventKey
*event
) {
691 guint16 length
= gtk_entry_get_text_length(entry
);
694 a
.i
= HideCompletion
;
702 static gboolean
inputbox_changed_cb(GtkEditable
*entry
, gpointer user_data
) {
704 char *text
= (char*)gtk_entry_get_text(GTK_ENTRY(entry
));
705 guint16 length
= gtk_entry_get_text_length(GTK_ENTRY(entry
));
706 gboolean forward
= FALSE
;
708 /* Update incremental search if the user changes the search text.
710 * Note: gtk_widget_is_focus() is a poor way to check if the change comes
711 * from the user. But if the entry is focused and the text is set
712 * through gtk_entry_set_text() in some asyncrounous operation,
713 * I would consider that a bug.
716 if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
> 1 && ((forward
= text
[0] == '/') || text
[0] == '?')) {
717 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
718 webkit_web_view_search_text(client
.gui
.webview
, &text
[1], searchoptions
& CaseSensitive
, forward
, searchoptions
& Wrapping
);
720 } else if (gtk_widget_is_focus(GTK_WIDGET(entry
)) && length
>= 1 &&
721 (text
[0] == '.' || text
[0] == ',' || text
[0] == ';')) {
725 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'f');", NULL
);
729 a
.s
= g_strconcat("hints.createHints('", text
+ 1, "', 'F');", NULL
);
736 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 's');", NULL
);
739 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'y');", NULL
);
742 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'f');", NULL
);
745 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'F');", NULL
);
747 case 'O': case 'T': case 'W':
748 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'O');", NULL
);
751 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'i');", NULL
);
754 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'I');", NULL
);
757 a
.s
= g_strconcat("hints.createHints('", text
+ 2, "', 'l');", NULL
);
762 client
.state
.count
= 0;
769 } else if (length
== 0) {
770 client
.state
.mode
= ModeNormal
;
772 a
.s
= g_strdup("hints.clearHints();");
775 client
.state
.count
= 0;
784 void fill_suggline(char * suggline
, const char * command
, const char *fill_with
) {
785 memset(suggline
, 0, 512);
786 strncpy(suggline
, command
, 512);
787 strncat(suggline
, " ", 1);
788 strncat(suggline
, fill_with
, 512 - strlen(suggline
) - 1);
791 GtkWidget
* fill_eventbox(const char * completion_line
) {
793 GtkWidget
*row_eventbox
, *el
;
795 char *markup
, *markup_tmp
;
797 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
798 row_eventbox
= gtk_event_box_new();
799 gdk_color_parse(completionbgcolor
[0], &color
);
800 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
801 el
= gtk_label_new(NULL
);
802 markup_tmp
= g_markup_escape_text(completion_line
, strlen(completion_line
));
803 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">",
804 markup_tmp
, "</span>", NULL
);
805 gtk_label_set_markup(GTK_LABEL(el
), markup
);
808 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
809 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
810 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
815 complete(const Arg
*arg
) {
816 char *str
, *p
, *s
, *markup
, *entry
, *searchfor
, command
[32] = "", suggline
[512] = "", **suggurls
;
817 size_t listlen
, len
, cmdlen
;
819 Listelement
*elementlist
= NULL
, *elementpointer
;
820 gboolean highlight
= FALSE
;
822 GtkWidget
*row_eventbox
, *el
;
825 static GtkWidget
*table
, *top_border
;
827 static char **suggestions
;
828 static GtkWidget
**widgets
;
829 static int n
= 0, m
, current
= -1;
830 Gui
*gui
= &client
.gui
;
832 str
= (char*)gtk_entry_get_text(GTK_ENTRY(gui
->inputbox
));
835 /* Get the length of the list of commands for completion. We need this to
836 * malloc/realloc correctly.
838 listlen
= LENGTH(commands
);
840 if ((len
== 0 || str
[0] != ':') && arg
->i
!= HideCompletion
)
843 if (arg
->i
!= HideCompletion
&& widgets
&& current
!= -1 && !strcmp(&str
[1], suggestions
[current
])) {
844 gdk_color_parse(completionbgcolor
[0], &color
);
845 gtk_widget_modify_bg(widgets
[current
], GTK_STATE_NORMAL
, &color
);
846 current
= (n
+ current
+ (arg
->i
== DirectionPrev
? -1 : 1)) % n
;
847 if ((arg
->i
== DirectionNext
&& current
== 0)
848 || (arg
->i
== DirectionPrev
&& current
== n
- 1))
854 gtk_widget_destroy(GTK_WIDGET(table
));
855 gtk_widget_destroy(GTK_WIDGET(top_border
));
862 if (arg
->i
== HideCompletion
)
865 } else if (arg
->i
== HideCompletion
)
868 prefix
= g_strdup(str
);
869 widgets
= malloc(sizeof(GtkWidget
*) * listlen
);
870 suggestions
= malloc(sizeof(char*) * listlen
);
871 top_border
= gtk_event_box_new();
872 gtk_widget_set_size_request(GTK_WIDGET(top_border
), 0, 1);
873 gdk_color_parse(completioncolor
[2], &color
);
874 gtk_widget_modify_bg(top_border
, GTK_STATE_NORMAL
, &color
);
875 table
= gtk_event_box_new();
876 gdk_color_parse(completionbgcolor
[0], &color
);
877 _table
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
879 if (strchr(str
, ' ') == NULL
) {
880 /* command completion */
881 listlen
= LENGTH(commands
);
882 for (i
= 0; i
< listlen
; i
++) {
883 if (commands
[i
].cmd
== NULL
)
885 cmdlen
= strlen(commands
[i
].cmd
);
886 if (!highlight
|| (n
< MAX_LIST_SIZE
&& len
- 1 <= cmdlen
&& !strncmp(&str
[1], commands
[i
].cmd
, len
- 1))) {
887 p
= s
= malloc(sizeof(char*) * (highlight
? sizeof(COMPLETION_TAG_OPEN
) + sizeof(COMPLETION_TAG_CLOSE
) - 1 : 1) + cmdlen
);
889 memcpy(p
, COMPLETION_TAG_OPEN
, sizeof(COMPLETION_TAG_OPEN
) - 1);
890 memcpy((p
+= sizeof(COMPLETION_TAG_OPEN
) - 1), &str
[1], len
- 1);
891 memcpy((p
+= len
- 1), COMPLETION_TAG_CLOSE
, sizeof(COMPLETION_TAG_CLOSE
) - 1);
892 p
+= sizeof(COMPLETION_TAG_CLOSE
) - 1;
894 memcpy(p
, &commands
[i
].cmd
[len
- 1], cmdlen
- len
+ 2);
895 row
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
896 row_eventbox
= gtk_event_box_new();
897 gtk_widget_modify_bg(row_eventbox
, GTK_STATE_NORMAL
, &color
);
898 el
= gtk_label_new(NULL
);
899 markup
= g_strconcat("<span font=\"", completionfont
[0], "\" color=\"", completioncolor
[0], "\">", s
, "</span>", NULL
);
901 gtk_label_set_markup(GTK_LABEL(el
), markup
);
903 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
904 gtk_box_pack_start(row
, el
, TRUE
, TRUE
, 2);
905 gtk_container_add(GTK_CONTAINER(row_eventbox
), GTK_WIDGET(row
));
906 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
907 suggestions
[n
] = commands
[i
].cmd
;
908 widgets
[n
++] = row_eventbox
;
912 entry
= (char *)malloc(512 * sizeof(char));
916 memset(entry
, 0, 512);
917 suggurls
= malloc(sizeof(char*) * listlen
);
918 if (suggurls
== NULL
) {
921 spacepos
= strcspn(str
, " ");
922 searchfor
= (str
+ spacepos
+ 1);
923 strncpy(command
, (str
+ 1), spacepos
- 1);
924 if (strlen(command
) == 3 && strncmp(command
, "set", 3) == 0) {
925 /* browser settings */
926 listlen
= LENGTH(browsersettings
);
927 for (i
= 0; i
< listlen
; i
++) {
928 if (n
< MAX_LIST_SIZE
&& strstr(browsersettings
[i
].name
, searchfor
) != NULL
) {
930 fill_suggline(suggline
, command
, browsersettings
[i
].name
);
931 /* FIXME(HP): This memory is never freed */
932 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
933 strncpy(suggurls
[n
], suggline
, 512);
934 suggestions
[n
] = suggurls
[n
];
935 row_eventbox
= fill_eventbox(suggline
);
936 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
937 widgets
[n
++] = row_eventbox
;
941 } else if (strlen(command
) == 2 && strncmp(command
, "qt", 2) == 0) {
942 /* completion on tags */
943 spacepos
= strcspn(str
, " ");
944 searchfor
= (str
+ spacepos
+ 1);
945 elementlist
= complete_list(searchfor
, 1, elementlist
);
947 /* URL completion: bookmarks */
948 elementlist
= complete_list(searchfor
, 0, elementlist
);
949 m
= count_list(elementlist
);
950 if (m
< MAX_LIST_SIZE
) {
951 /* URL completion: history */
952 elementlist
= complete_list(searchfor
, 2, elementlist
);
955 elementpointer
= elementlist
;
956 while (elementpointer
!= NULL
) {
957 fill_suggline(suggline
, command
, elementpointer
->element
);
958 /* FIXME(HP): This memory is never freed */
959 suggurls
[n
] = (char *)malloc(sizeof(char) * 512 + 1);
960 strncpy(suggurls
[n
], suggline
, 512);
961 suggestions
[n
] = suggurls
[n
];
962 row_eventbox
= fill_eventbox(suggline
);
963 gtk_box_pack_start(_table
, GTK_WIDGET(row_eventbox
), FALSE
, FALSE
, 0);
964 widgets
[n
++] = row_eventbox
;
965 elementpointer
= elementpointer
->next
;
966 if (n
>= MAX_LIST_SIZE
)
969 free_list(elementlist
);
970 if (suggurls
!= NULL
) {
979 /* TA: FIXME - this needs rethinking entirely. */
981 GtkWidget
**widgets_temp
= realloc(widgets
, sizeof(*widgets
) * n
);
982 if (widgets_temp
== NULL
&& widgets
== NULL
) {
983 fprintf(stderr
, "Couldn't realloc() widgets\n");
986 widgets
= widgets_temp
;
987 char **suggestions_temp
= realloc(suggestions
, sizeof(*suggestions
) * n
);
988 if (suggestions_temp
== NULL
&& suggestions
== NULL
) {
989 fprintf(stderr
, "Couldn't realloc() suggestions\n");
992 suggestions
= suggestions_temp
;
995 gdk_color_parse(completionbgcolor
[1], &color
);
996 gtk_widget_modify_bg(table
, GTK_STATE_NORMAL
, &color
);
997 el
= gtk_label_new(NULL
);
998 gtk_misc_set_alignment(GTK_MISC(el
), 0, 0);
999 markup
= g_strconcat("<span font=\"", completionfont
[1], "\" color=\"", completioncolor
[1], "\">No Completions</span>", NULL
);
1000 gtk_label_set_markup(GTK_LABEL(el
), markup
);
1002 gtk_box_pack_start(_table
, GTK_WIDGET(el
), FALSE
, FALSE
, 0);
1004 gtk_box_pack_start(gui
->box
, GTK_WIDGET(top_border
), FALSE
, FALSE
, 0);
1005 gtk_container_add(GTK_CONTAINER(table
), GTK_WIDGET(_table
));
1006 gtk_box_pack_start(gui
->box
, GTK_WIDGET(table
), FALSE
, FALSE
, 0);
1007 gtk_widget_show_all(GTK_WIDGET(table
));
1008 gtk_widget_show_all(GTK_WIDGET(top_border
));
1011 current
= arg
->i
== DirectionPrev
? n
- 1 : 0;
1013 if (current
!= -1) {
1014 gdk_color_parse(completionbgcolor
[2], &color
);
1015 gtk_widget_modify_bg(GTK_WIDGET(widgets
[current
]), GTK_STATE_NORMAL
, &color
);
1016 s
= g_strconcat(":", suggestions
[current
], NULL
);
1017 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), s
);
1020 gtk_entry_set_text(GTK_ENTRY(gui
->inputbox
), prefix
);
1021 gtk_editable_set_position(GTK_EDITABLE(gui
->inputbox
), -1);
1026 descend(const Arg
*arg
) {
1027 char *source
= (char*)webkit_web_view_get_uri(client
.gui
.webview
), *p
= &source
[0], *new;
1029 client
.state
.count
= client
.state
.count
? client
.state
.count
: 1;
1033 if (arg
->i
== Rootdir
) {
1034 for (i
= 0; i
< 3; i
++) /* get to the third slash */
1035 if (!(p
= strchr(++p
, '/')))
1036 return TRUE
; /* if we cannot find it quit */
1038 len
= strlen(source
);
1039 if (!len
) /* if string is empty quit */
1041 p
= source
+ len
; /* start at the end */
1042 if (*(p
- 1) == '/') /* /\/$/ is not an additional level */
1043 ++client
.state
.count
;
1044 for (i
= 0; i
< client
.state
.count
; i
++)
1045 while(*(p
--) != '/' || *p
== '/') /* count /\/+/ as one slash */
1046 if (p
== source
) /* if we reach the first char pointer quit */
1048 ++p
; /* since we do p-- in the while, we are pointing at
1049 the char before the slash, so +1 */
1051 len
= p
- source
+ 1; /* new length = end - start + 1 */
1052 new = malloc(len
+ 1);
1053 memcpy(new, source
, len
);
1055 webkit_web_view_load_uri(client
.gui
.webview
, new);
1061 echo(const Arg
*arg
) {
1062 int index
= !arg
->s
? 0 : arg
->i
& (~NoAutoHide
);
1064 if (index
< Info
|| index
> Error
)
1067 if (!gtk_widget_is_focus(GTK_WIDGET(client
.gui
.inputbox
))) {
1068 set_widget_font_and_color(client
.gui
.inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1069 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), !arg
->s
? "" : arg
->s
);
1076 open_inspector(const Arg
* arg
) {
1077 gboolean inspect_enabled
;
1078 WebKitWebSettings
*settings
;
1079 State
*state
= &client
.state
;
1081 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
1082 g_object_get(G_OBJECT(settings
), "enable-developer-extras", &inspect_enabled
, NULL
);
1083 if (inspect_enabled
) {
1084 if (state
->is_inspecting
) {
1085 webkit_web_inspector_close(client
.gui
.inspector
);
1087 webkit_web_inspector_show(client
.gui
.inspector
);
1091 echo_message(Error
, "Webinspector is not enabled");
1097 input(const Arg
*arg
) {
1099 client
.state
.count
= 0;
1103 GtkWidget
*inputbox
= client
.gui
.inputbox
;
1105 /* if inputbox hidden, show it again */
1106 if (!gtk_widget_get_visible(inputbox
))
1107 gtk_widget_set_visible(inputbox
, TRUE
);
1111 /* Set the colour and font back to the default, so that we don't still
1112 * maintain a red colour from a warning from an end of search indicator,
1115 set_widget_font_and_color(inputbox
, urlboxfont
[index
], urlboxbgcolor
[index
], urlboxcolor
[index
]);
1117 /* to avoid things like :open URL :open URL2 or :open :open URL */
1118 gtk_entry_set_text(GTK_ENTRY(inputbox
), "");
1119 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), arg
->s
, -1, &pos
);
1120 if (arg
->i
& InsertCurrentURL
&& (url
= webkit_web_view_get_uri(client
.gui
.webview
)))
1121 gtk_editable_insert_text(GTK_EDITABLE(inputbox
), url
, -1, &pos
);
1123 gtk_widget_grab_focus(inputbox
);
1124 gtk_editable_set_position(GTK_EDITABLE(inputbox
), -1);
1126 if (arg
->s
[0] == '.' || arg
->s
[0] == ',' || arg
->s
[0] == ';') {
1127 client
.state
.mode
= ModeHints
;
1129 switch (arg
->s
[0]) {
1131 a
.s
= g_strdup("hints.createHints('', 'f');");
1135 a
.s
= g_strdup("hints.createHints('', 'F');");
1141 switch (arg
->s
[1]) {
1143 a
.s
= g_strdup("hints.createHints('', 's');");
1146 a
.s
= g_strdup("hints.createHints('', 'y');");
1149 a
.s
= g_strdup("hints.createHints('', 'f');");
1152 a
.s
= g_strdup("hints.createHints('', 'F');");
1154 case 'O': case 'T': case 'W':
1155 a
.s
= g_strdup("hints.createHints('', 'O');");
1158 a
.s
= g_strdup("hints.createHints('', 'i');");
1161 a
.s
= g_strdup("hints.createHints('', 'I');");
1164 a
.s
= g_strdup("hints.createHints('', 'l');");
1170 client
.state
.count
= 0;
1181 navigate(const Arg
*arg
) {
1182 if (arg
->i
& NavigationForwardBack
)
1183 webkit_web_view_go_back_or_forward(client
.gui
.webview
, (arg
->i
== NavigationBack
? -1 : 1) * (client
.state
.count
? client
.state
.count
: 1));
1184 else if (arg
->i
& NavigationReloadActions
)
1185 (arg
->i
== NavigationReload
? webkit_web_view_reload
: webkit_web_view_reload_bypass_cache
)(client
.gui
.webview
);
1187 webkit_web_view_stop_loading(client
.gui
.webview
);
1192 number(const Arg
*arg
) {
1193 const char *source
= webkit_web_view_get_uri(client
.gui
.webview
);
1194 char *uri
, *p
, *new;
1195 int number
, diff
= (client
.state
.count
? client
.state
.count
: 1) * (arg
->i
== Increment
? 1 : -1);
1199 uri
= g_strdup(source
); /* copy string */
1201 while(*p
!= '\0') /* goto the end of the string */
1204 while(*p
>= '0' && *p
<= '9') /* go back until non number char is reached */
1206 if (*(++p
) == '\0') { /* if no numbers were found abort */
1210 number
= atoi(p
) + diff
; /* apply diff on number */
1212 new = g_strdup_printf("%s%d", uri
, number
); /* create new uri */
1213 webkit_web_view_load_uri(client
.gui
.webview
, new);
1220 open_arg(const Arg
*arg
) {
1222 char *s
= arg
->s
, *p
= NULL
, *new;
1223 Arg a
= { .i
= NavigationReload
};
1225 char *search_uri
, *search_term
;
1226 struct stat statbuf
;
1228 if (client
.state
.embed
) {
1230 snprintf(winid
, LENGTH(winid
), "%u", (gint
)client
.state
.embed
);
1244 else if (arg
->i
== TargetCurrent
) {
1245 while(*s
== ' ') /* strip leading whitespace */
1247 p
= (s
+ strlen(s
) - 1);
1248 while(*p
== ' ') /* strip trailing whitespace */
1253 /* check for external handlers */
1254 if (open_handler(s
))
1256 /* check for search engines */
1259 /* shortcut without search term */
1262 /* search term given */
1266 search_uri
= find_uri_for_searchengine(s
);
1267 if (search_uri
!= NULL
) {
1269 search_term
= soup_uri_encode(p
+1, "&");
1270 new = g_strdup_printf(search_uri
, search_term
);
1271 g_free(search_term
);
1273 if (!strstr(search_uri
, "%s"))
1274 new = g_strdup_printf(search_uri
);
1276 /* the search engine definition expected an argument */
1277 new = g_strdup_printf(search_uri
, "");
1284 if (len
> 3 && strstr(s
, "://")) { /* valid url? */
1285 p
= new = g_malloc(len
+ 1);
1286 while(*s
!= '\0') { /* strip whitespaces */
1292 } else if (!stat(s
, &statbuf
)) { /* prepend "file://" */
1293 char *rpath
= realpath(s
, NULL
);
1294 if (rpath
!= NULL
) {
1295 len
= strlen(rpath
);
1296 new = g_malloc(sizeof("file://") + len
);
1297 sprintf(new, "file://%s", rpath
);
1300 new = g_malloc(sizeof("file://") + len
);
1301 sprintf(new, "file://%s", s
);
1303 } else if (space
> 0 || !strchr(s
, '.')) { /* whitespaces or no dot? */
1304 search_uri
= find_uri_for_searchengine(defaultsearch
);
1305 if (search_uri
!= NULL
) {
1306 search_term
= soup_uri_encode(s
, "&");
1307 new = g_strdup_printf(search_uri
, search_term
);
1308 g_free(search_term
);
1310 } else { /* prepend "http://" */
1311 new = g_malloc(sizeof("http://") + len
);
1312 strcpy(new, "http://");
1313 memcpy(&new[sizeof("http://") - 1], s
, len
+ 1);
1316 webkit_web_view_load_uri(client
.gui
.webview
, new);
1319 g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
, NULL
, NULL
, NULL
, NULL
);
1324 open_remembered(const Arg
*arg
)
1326 Arg a
= {arg
->i
, client
.state
.rememberedURI
};
1328 if (strcmp(client
.state
.rememberedURI
, "")) {
1335 yank(const Arg
*arg
) {
1336 const char *url
, *content
;
1338 if (arg
->i
& SourceSelection
) {
1339 webkit_web_view_copy_clipboard(client
.gui
.webview
);
1340 if (arg
->i
& ClipboardPrimary
)
1341 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1342 if (!content
&& arg
->i
& ClipboardGTK
)
1343 content
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1345 echo_message(Info
, "Yanked %s", content
);
1346 g_free((gpointer
*)content
);
1349 if (arg
->i
& SourceURL
) {
1350 url
= webkit_web_view_get_uri(client
.gui
.webview
);
1357 echo_message(Info
, "Yanked %s", url
);
1358 if (arg
->i
& ClipboardPrimary
)
1359 gtk_clipboard_set_text(client
.state
.clipboards
[0], url
, -1);
1360 if (arg
->i
& ClipboardGTK
)
1361 gtk_clipboard_set_text(client
.state
.clipboards
[1], url
, -1);
1367 paste(const Arg
*arg
) {
1368 Arg a
= { .i
= arg
->i
& TargetNew
, .s
= NULL
};
1370 /* If we're over a link, open it in a new target. */
1371 if (strlen(client
.state
.rememberedURI
) > 0) {
1372 Arg new_target
= { .i
= TargetNew
, .s
= arg
->s
};
1373 open_arg(&new_target
);
1377 if (arg
->i
& ClipboardPrimary
)
1378 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[0]);
1379 if (!a
.s
&& arg
->i
& ClipboardGTK
)
1380 a
.s
= gtk_clipboard_wait_for_text(client
.state
.clipboards
[1]);
1389 quit(const Arg
*arg
) {
1391 const char *filename
;
1392 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1394 /* write last URL into status file for recreation with "u" */
1395 filename
= g_strdup_printf("%s", client
.config
.config_base
);
1396 filename
= g_strdup_printf(private_mode
? "/dev/null" : CLOSED_URL_FILENAME
);
1397 f
= fopen(filename
, "w");
1398 g_free((gpointer
*)filename
);
1400 fprintf(f
, "%s", uri
);
1409 revive(const Arg
*arg
) {
1411 const char *filename
;
1412 char buffer
[512] = "";
1413 Arg a
= { .i
= TargetNew
, .s
= NULL
};
1414 /* get the URL of the window which has been closed last */
1415 filename
= g_strdup_printf(CLOSED_URL_FILENAME
);
1416 f
= fopen(filename
, "r");
1417 g_free((gpointer
*)filename
);
1419 fgets(buffer
, 512, f
);
1422 if (strlen(buffer
) > 0) {
1431 gboolean
print_frame(const Arg
*arg
)
1433 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1434 webkit_web_frame_print (frame
);
1439 search(const Arg
*arg
) {
1440 State
*state
= &client
.state
;
1441 state
->count
= state
->count
? state
->count
: 1;
1442 gboolean success
, direction
= arg
->i
& DirectionPrev
;
1445 if (state
->search_handle
) {
1446 g_free(state
->search_handle
);
1448 state
->search_handle
= g_strdup(arg
->s
);
1450 if (!state
->search_handle
)
1452 if (arg
->i
& DirectionAbsolute
)
1453 state
->search_direction
= direction
;
1455 direction
^= state
->search_direction
;
1457 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, FALSE
);
1459 if (arg
->i
& Wrapping
) {
1460 success
= webkit_web_view_search_text(client
.gui
.webview
, state
->search_handle
, arg
->i
& CaseSensitive
, direction
, TRUE
);
1462 echo_message(Warning
, "search hit %s, continuing at %s",
1463 direction
? "BOTTOM" : "TOP",
1464 direction
? "TOP" : "BOTTOM");
1470 } while(--state
->count
);
1472 echo_message(Error
, "Pattern not found: %s", state
->search_handle
);
1478 set(const Arg
*arg
) {
1481 if (client
.state
.search_handle
) {
1482 g_free(client
.state
.search_handle
);
1483 client
.state
.search_handle
= NULL
;
1484 webkit_web_view_unmark_text_matches(client
.gui
.webview
);
1486 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), "");
1487 gtk_widget_grab_focus(GTK_WIDGET(client
.gui
.webview
));
1489 case ModePassThrough
:
1490 echo_message(Info
| NoAutoHide
, "-- PASS THROUGH --");
1493 echo_message(Info
| NoAutoHide
, "-- PASS TROUGH (next) --");
1495 case ModeInsert
: /* should not be called manually but automatically */
1496 /* make sure we leaf focus from inputbox to show the new mode */
1497 gtk_widget_grab_focus(GTK_WIDGET(client
.gui
.webview
));
1498 echo_message(Info
| NoAutoHide
, "-- INSERT --");
1503 client
.state
.mode
= arg
->i
;
1508 jsapi_ref_to_string(JSContextRef context
, JSValueRef ref
) {
1509 JSStringRef string_ref
;
1513 string_ref
= JSValueToStringCopy(context
, ref
, NULL
);
1514 length
= JSStringGetMaximumUTF8CStringSize(string_ref
);
1515 string
= g_new(gchar
, length
);
1516 JSStringGetUTF8CString(string_ref
, string
, length
);
1517 JSStringRelease(string_ref
);
1522 jsapi_evaluate_script(const gchar
*script
, gchar
**value
, gchar
**message
) {
1523 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
1524 JSGlobalContextRef context
= webkit_web_frame_get_global_context(frame
);
1526 JSValueRef val
, exception
;
1528 str
= JSStringCreateWithUTF8CString(script
);
1529 val
= JSEvaluateScript(context
, str
, JSContextGetGlobalObject(context
), NULL
, 0, &exception
);
1530 JSStringRelease(str
);
1532 *message
= jsapi_ref_to_string(context
, exception
);
1534 *value
= jsapi_ref_to_string(context
, val
);
1538 quickmark(const Arg
*a
) {
1541 char *fn
= g_strdup_printf(QUICKMARK_FILE
);
1543 fp
= fopen(fn
, "r");
1548 if (fp
!= NULL
&& b
< 10) {
1549 for( i
=0; i
< b
; ++i
) {
1553 fgets(buf
, 100, fp
);
1555 char *ptr
= strrchr(buf
, '\n');
1558 Arg x
= { .s
= buf
};
1559 return open_arg(&x
);
1561 echo_message(Error
, "Quickmark %d not defined", b
);
1564 } else { return false; }
1568 script(const Arg
*arg
) {
1569 gchar
*value
= NULL
, *message
= NULL
;
1570 char text
[BUF_SIZE
] = "";
1572 WebKitNetworkRequest
*request
;
1573 WebKitDownload
*download
;
1576 set_error("Missing argument.");
1579 jsapi_evaluate_script(arg
->s
, &value
, &message
);
1587 if (arg
->i
!= Silent
&& value
) {
1588 echo_message(arg
->i
, value
);
1590 /* switch mode according to scripts return value */
1592 if (strncmp(value
, "done;", 5) == 0) {
1595 } else if (strncmp(value
, "insert;", 7) == 0) {
1598 client
.state
.manual_focus
= TRUE
;
1599 } else if (strncmp(value
, "save;", 5) == 0) {
1600 /* forced download */
1603 request
= webkit_network_request_new((value
+ 5));
1604 download
= webkit_download_new(request
);
1605 webview_download_cb(client
.gui
.webview
, download
, (gpointer
*)NULL
);
1606 } else if (strncmp(value
, "yank;", 5) == 0) {
1607 /* yank link URL to clipboard */
1610 a
.i
= ClipboardPrimary
| ClipboardGTK
;
1613 } else if (strncmp(value
, "colon;", 6) == 0) {
1614 /* use link URL for colon command */
1615 strncpy(text
, (char *)gtk_entry_get_text(GTK_ENTRY(client
.gui
.inputbox
)), 1023);
1620 a
.s
= g_strconcat(":open ", (value
+ 6), NULL
);
1623 a
.s
= g_strconcat(":tabopen ", (value
+ 6), NULL
);
1630 } else if (strncmp(value
, "open;", 5) == 0 || strncmp(value
, "tabopen;", 8) == 0) {
1631 /* TODO: open element */
1634 if (strncmp(value
, "open;", 5) == 0)
1635 a
.i
= TargetCurrent
;
1638 a
.s
= (strchr(value
, ';') + 1);
1640 } else if (strncmp(value
, "show_link;", 10) == 0) {
1643 char *link
= strchr(value
, ';') + 1;
1645 memset(client
.state
.rememberedURI
, 0, BUF_SIZE
);
1648 } else if (strncmp(value
, "error;", 6) == 0) {
1658 scroll(const Arg
*arg
) {
1659 GtkAdjustment
*adjust
= (arg
->i
& OrientationHoriz
) ? client
.gui
.adjust_h
: client
.gui
.adjust_v
;
1660 int max
= gtk_adjustment_get_upper(adjust
) - gtk_adjustment_get_page_size(adjust
);
1661 float val
= gtk_adjustment_get_value(adjust
) / max
* 100;
1662 int direction
= (arg
->i
& (1 << 2)) ? 1 : -1;
1663 unsigned int count
= client
.state
.count
;
1665 if ((direction
== 1 && val
< 100) || (direction
== -1 && val
> 0)) {
1666 if (arg
->i
& ScrollMove
)
1667 gtk_adjustment_set_value(adjust
, gtk_adjustment_get_value(adjust
) +
1668 direction
* /* direction */
1669 ((arg
->i
& UnitLine
|| (arg
->i
& UnitBuffer
&& count
)) ? (scrollstep
* (count
? count
: 1)) : (
1670 arg
->i
& UnitBuffer
? gtk_adjustment_get_page_size(adjust
) / 2 :
1671 (count
? count
: 1) * (gtk_adjustment_get_page_size(adjust
) -
1672 (gtk_adjustment_get_page_size(adjust
) > pagingkeep
? pagingkeep
: 0)))));
1674 gtk_adjustment_set_value(adjust
,
1675 ((direction
== 1) ? gtk_adjustment_get_upper
: gtk_adjustment_get_lower
)(adjust
));
1682 zoom(const Arg
*arg
) {
1683 unsigned int count
= client
.state
.count
;
1685 webkit_web_view_set_full_content_zoom(client
.gui
.webview
, (arg
->i
& ZoomFullContent
) > 0);
1686 webkit_web_view_set_zoom_level(client
.gui
.webview
, (arg
->i
& ZoomOut
) ?
1687 webkit_web_view_get_zoom_level(client
.gui
.webview
) +
1688 (((float)(count
? count
: 1)) * (arg
->i
& (1 << 1) ? 1.0 : -1.0) * client
.config
.zoomstep
) :
1689 (count
? (float)count
/ 100.0 : 1.0));
1694 fake_key_event(const Arg
*a
) {
1695 if(!client
.state
.embed
) {
1699 if ( (xdpy
= XOpenDisplay(NULL
)) == NULL
) {
1700 echo_message(Error
, "Couldn't find the XDisplay.");
1706 xk
.subwindow
= None
;
1707 xk
.time
= CurrentTime
;
1708 xk
.same_screen
= True
;
1709 xk
.x
= xk
.y
= xk
.x_root
= xk
.y_root
= 1;
1710 xk
.window
= client
.state
.embed
;
1714 echo_message(Error
, "Zero pointer as argument! Check your config.h");
1719 if( (keysym
= XStringToKeysym(a
->s
)) == NoSymbol
) {
1720 echo_message(Error
, "Couldn't translate %s to keysym", a
->s
);
1724 if( (xk
.keycode
= XKeysymToKeycode(xdpy
, keysym
)) == NoSymbol
) {
1725 echo_message(Error
, "Couldn't translate keysym to keycode");
1730 if( !XSendEvent(xdpy
, client
.state
.embed
, True
, KeyPressMask
, (XEvent
*)&xk
) ) {
1731 echo_message(Error
, "XSendEvent failed");
1740 commandhistoryfetch(const Arg
*arg
) {
1741 State
*state
= &client
.state
;
1742 const int length
= g_list_length(client
.state
.commandhistory
);
1743 gchar
*input_message
= NULL
;
1746 if (arg
->i
== DirectionPrev
) {
1747 state
->commandpointer
= (length
+ state
->commandpointer
- 1) % length
;
1749 state
->commandpointer
= (length
+ state
->commandpointer
+ 1) % length
;
1752 const char* command
= (char *)g_list_nth_data(state
->commandhistory
, state
->commandpointer
);
1753 input_message
= g_strconcat(":", command
, NULL
);
1754 gtk_entry_set_text(GTK_ENTRY(client
.gui
.inputbox
), input_message
);
1755 g_free(input_message
);
1756 gtk_editable_set_position(GTK_EDITABLE(client
.gui
.inputbox
), -1);
1764 bookmark(const Arg
*arg
) {
1766 const char *filename
;
1767 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1768 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1769 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
1770 f
= fopen(filename
, "a");
1771 g_free((gpointer
*)filename
);
1772 if (uri
== NULL
|| strlen(uri
) == 0) {
1773 set_error("No URI found to bookmark.");
1777 fprintf(f
, "%s", uri
);
1778 if (title
!= NULL
) {
1779 fprintf(f
, "%s", " ");
1780 fprintf(f
, "%s", title
);
1782 if (arg
->s
&& strlen(arg
->s
)) {
1783 build_taglist(arg
, f
);
1785 fprintf(f
, "%s", "\n");
1787 echo_message(Info
, "Bookmark saved");
1790 set_error("Bookmarks file not found.");
1798 const char *filename
;
1799 const char *uri
= webkit_web_view_get_uri(client
.gui
.webview
);
1800 const char *title
= webkit_web_view_get_title(client
.gui
.webview
);
1801 char *entry
, buffer
[512], *new;
1803 gboolean finished
= FALSE
;
1805 if (title
!= NULL
) {
1806 entry
= malloc((strlen(uri
) + strlen(title
) + 2) * sizeof(char));
1807 memset(entry
, 0, strlen(uri
) + strlen(title
) + 2);
1809 entry
= malloc((strlen(uri
) + 1) * sizeof(char));
1810 memset(entry
, 0, strlen(uri
) + 1);
1812 if (entry
!= NULL
) {
1813 strncpy(entry
, uri
, strlen(uri
));
1814 if (title
!= NULL
) {
1815 strncat(entry
, " ", 1);
1816 strncat(entry
, title
, strlen(title
));
1819 filename
= g_strdup_printf(private_mode
? "/dev/null" : HISTORY_STORAGE_FILENAME
);
1820 f
= fopen(filename
, "r");
1822 new = (char *)malloc(HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1824 memset(new, 0, HISTORY_MAX_ENTRIES
* 512 * sizeof(char) + 1);
1825 /* newest entries go on top */
1826 strncpy(new, entry
, strlen(entry
));
1827 strncat(new, "\n", 1);
1828 /* retain at most HISTORY_MAX_ENTIRES - 1 old entries */
1829 while (finished
!= TRUE
) {
1830 if ((char *)NULL
== fgets(buffer
, 512, f
)) {
1831 /* check if end of file was reached / error occured */
1835 /* end of file reached */
1839 /* compare line (-1 because of newline character) */
1840 if (n
!= strlen(buffer
) - 1 || strncmp(entry
, buffer
, n
) != 0) {
1841 /* if the URI is already in history; we put it on top and skip it here */
1842 strncat(new, buffer
, 512);
1845 if ((i
+ 1) >= HISTORY_MAX_ENTRIES
) {
1851 f
= fopen(filename
, "w");
1852 g_free((gpointer
*)filename
);
1854 fprintf(f
, "%s", new);
1863 if (entry
!= NULL
) {
1872 view_source(const Arg
* arg
) {
1873 gboolean current_mode
= webkit_web_view_get_view_source_mode(client
.gui
.webview
);
1874 webkit_web_view_set_view_source_mode(client
.gui
.webview
, !current_mode
);
1875 webkit_web_view_reload(client
.gui
.webview
);
1879 /* open an external editor defined by the protocol handler for
1880 vimprobableedit on a text box or similar */
1882 open_editor(const Arg
*arg
) {
1886 gchar
*value
= NULL
, *message
= NULL
, *tag
= NULL
, *edit_url
= NULL
;
1887 gchar
*temp_file_name
= g_strdup_printf("%s/vimprobableeditXXXXXX",
1889 int temp_file_handle
= -1;
1891 /* check if active element is suitable for text editing */
1892 jsapi_evaluate_script("document.activeElement.tagName", &value
, &message
);
1893 if (value
== NULL
) {
1897 tag
= g_strdup(value
);
1898 if (strcmp(tag
, "INPUT") == 0) {
1899 /* extra check: type == text */
1900 jsapi_evaluate_script("document.activeElement.type", &value
, &message
);
1901 if (strcmp(value
, "text") != 0) {
1908 } else if (strcmp(tag
, "TEXTAREA") != 0) {
1913 jsapi_evaluate_script("document.activeElement.value", &value
, &message
);
1914 text
= g_strdup(value
);
1920 jsapi_evaluate_script("editElem = document.activeElement", &value
, &message
);
1924 /* write text into temporary file */
1925 temp_file_handle
= mkstemp(temp_file_name
);
1926 if (temp_file_handle
== -1) {
1927 message
= g_strdup_printf("Could not create temporary file: %s",
1929 echo_message(Error
, message
);
1935 if (write(temp_file_handle
, text
, strlen(text
)) != strlen(text
)) {
1936 message
= g_strdup_printf("Short write to temporary file: %s",
1938 echo_message(Error
, message
);
1944 close(temp_file_handle
);
1948 edit_url
= g_strdup_printf("vimprobableedit:%s", temp_file_name
);
1949 success
= open_handler_pid(edit_url
, &child_pid
);
1952 echo_message(Error
, "External editor open failed (no handler for"
1953 " vimprobableedit protocol?)");
1954 unlink(temp_file_name
);
1960 /* mark the active text box as "under processing" */
1961 jsapi_evaluate_script(
1962 "editElem.disabled = true;"
1963 "editElem.originalBackground = "
1964 " editElem.style.background;"
1965 "editElem.style.background = '#aaaaaa';"
1968 g_child_watch_add(child_pid
, _resume_from_editor
, temp_file_name
);
1970 /* temp_file_name is freed in _resume_from_editor */
1978 /* pick up from where open_editor left the work to the glib event loop.
1980 This is called when the external editor exits.
1982 The data argument points to allocated memory containing the temporary file
1985 _resume_from_editor(GPid child_pid
, int child_status
, gpointer data
) {
1987 GString
*set_value_js
= g_string_new(
1988 "editElem.value = \"");
1989 g_spawn_close_pid(child_pid
);
1990 gchar
*value
= NULL
, *message
= NULL
;
1991 gchar
*temp_file_name
= data
;
1992 gchar buffer
[BUF_SIZE
] = "";
1993 gchar
*buf_ptr
= buffer
;
1996 jsapi_evaluate_script(
1997 "editElem.disabled = true;"
1998 "editElem.style.background = '#aaaaaa';"
2004 echo_message(Error
, "External editor returned with non-zero status,"
2005 " discarding edits.");
2009 /* re-read the new contents of the file and put it into the HTML element */
2010 if (!access(temp_file_name
, R_OK
) == 0) {
2011 message
= g_strdup_printf("Could not access temporary file: %s",
2015 fp
= fopen(temp_file_name
, "r");
2017 /* this would be too weird to even emit an error message */
2020 jsapi_evaluate_script("editElem.value = '';",
2025 while (EOF
!= (char_read
= fgetc(fp
))) {
2026 if (char_read
== '\n') {
2029 } else if (char_read
== '"') {
2033 *buf_ptr
++ = char_read
;
2035 /* ship out as the buffer when space gets tight. This has
2036 fuzz to save on thinking, plus we have enough space for the
2037 trailing "; in any case. */
2038 if (buf_ptr
-buffer
>=BUF_SIZE
-10) {
2040 g_string_append(set_value_js
, buffer
);
2047 g_string_append(set_value_js
, buffer
);
2050 jsapi_evaluate_script(set_value_js
->str
, &value
, &message
);
2052 /* Fall through, error and normal exit are identical */
2054 jsapi_evaluate_script(
2055 "editElem.disabled = false;"
2056 "editElem.style.background ="
2057 " editElem.originalBackground;"
2060 g_string_free(set_value_js
, TRUE
);
2061 unlink(temp_file_name
);
2062 g_free(temp_file_name
);
2068 focus_input(const Arg
*arg
) {
2071 a
.s
= g_strdup("hints.focusInput();");
2076 client
.state
.manual_focus
= TRUE
;
2084 a
.s
= g_strdup("hints.clearFocus();");
2094 browser_settings(const Arg
*arg
) {
2097 set_error("Missing argument.");
2100 strncpy(line
, arg
->s
, 254);
2101 if (process_set_line(line
))
2104 set_error("Invalid setting.");
2110 search_word(int whichword
) {
2112 static char word
[240];
2113 char *c
= my_pair
.line
;
2115 while (isspace(*c
) && *c
)
2118 switch (whichword
) {
2120 while (*c
&& !isspace (*c
) && *c
!= '=' && k
< 240) {
2125 strncpy(my_pair
.what
, word
, 20);
2128 while (*c
&& k
< 240) {
2133 strncpy(my_pair
.value
, word
, 240);
2141 process_set_line(char *line
) {
2145 WebKitWebSettings
*settings
;
2147 settings
= webkit_web_view_get_settings(client
.gui
.webview
);
2148 my_pair
.line
= line
;
2150 if (!strlen(my_pair
.what
))
2153 while (isspace(*c
) && *c
)
2156 if (*c
== ':' || *c
== '=')
2162 listlen
= LENGTH(browsersettings
);
2163 for (i
= 0; i
< listlen
; i
++) {
2164 if (strlen(browsersettings
[i
].name
) == strlen(my_pair
.what
) && strncmp(browsersettings
[i
].name
, my_pair
.what
, strlen(my_pair
.what
)) == 0) {
2165 /* mandatory argument not provided */
2166 if (strlen(my_pair
.value
) == 0)
2168 /* process qmark? */
2169 if (strlen(my_pair
.what
) == 5 && strncmp("qmark", my_pair
.what
, 5) == 0) {
2170 return (process_save_qmark(my_pair
.value
, client
.gui
.webview
));
2172 /* interpret boolean values */
2173 if (browsersettings
[i
].boolval
) {
2174 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) {
2176 } 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) {
2181 } else if (browsersettings
[i
].colourval
) {
2182 /* interpret as hexadecimal colour */
2183 if (!parse_colour(my_pair
.value
)) {
2187 if (browsersettings
[i
].var
!= NULL
) {
2188 strncpy(browsersettings
[i
].var
, my_pair
.value
, MAX_SETTING_SIZE
);
2189 if (strlen(my_pair
.value
) > MAX_SETTING_SIZE
- 1) {
2190 /* in this case, \0 will not have been copied */
2191 browsersettings
[i
].var
[MAX_SETTING_SIZE
- 1] = '\0';
2192 /* in case this string is also used for a webkit setting, make sure it's consistent */
2193 my_pair
.value
[MAX_SETTING_SIZE
- 1] = '\0';
2194 echo_message(Info
, "String too long; automatically truncated!");
2197 if (strlen(browsersettings
[i
].webkit
) > 0) {
2198 /* activate appropriate webkit setting */
2199 if (browsersettings
[i
].boolval
) {
2200 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, boolval
, NULL
);
2201 } else if (browsersettings
[i
].intval
) {
2202 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, atoi(my_pair
.value
), NULL
);
2204 g_object_set((GObject
*)settings
, browsersettings
[i
].webkit
, my_pair
.value
, NULL
);
2206 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2209 if (strlen(my_pair
.what
) == 14) {
2210 if (strncmp("acceptlanguage", my_pair
.what
, 14) == 0) {
2211 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2212 } else if (strncmp("completioncase", my_pair
.what
, 14) == 0) {
2213 complete_case_sensitive
= boolval
;
2215 } else if (strlen(my_pair
.what
) == 5 && strncmp("proxy", my_pair
.what
, 5) == 0) {
2216 toggle_proxy(boolval
);
2217 } else if (strlen(my_pair
.what
) == 10 && strncmp("scrollbars", my_pair
.what
, 10) == 0) {
2218 toggle_scrollbars(boolval
);
2219 } else if (strlen(my_pair
.what
) == 9 && strncmp("statusbar", my_pair
.what
, 9) == 0) {
2220 gtk_widget_set_visible(GTK_WIDGET(client
.gui
.statusbar
), boolval
);
2221 } else if (strlen(my_pair
.what
) == 8 && strncmp("inputbox", my_pair
.what
, 8) == 0) {
2222 gtk_widget_set_visible(client
.gui
.inputbox
, boolval
);
2223 } else if (strlen(my_pair
.what
) == 11 && strncmp("escapeinput", my_pair
.what
, 11) == 0) {
2224 escape_input_on_load
= boolval
;
2225 } else if (strlen(my_pair
.what
) == 7 && strncmp("private", my_pair
.what
, 7) == 0) {
2226 /* Store the state of the last 'on' cookie state before toggling it off */
2228 /* Only update this LastOn state if private mode was false before */
2229 if (!private_mode
) {
2230 CookiePolicyLastOn
= CookiePolicy
;
2232 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
2234 /* If here, we are not in private_mode, so restore the cookie policy */
2235 CookiePolicy
= CookiePolicyLastOn
;
2237 soup_cookie_jar_set_accept_policy(client
.net
.session_cookie_jar
, CookiePolicy
);
2238 private_mode
= boolval
; /* Lastly, set the private_mode for use in other areas */
2239 } else if (strlen(my_pair
.what
) == 7 && strncmp("cookies", my_pair
.what
, 7) == 0) {
2241 if (strncmp(my_pair
.value
, "on", 2) == 0 || strncmp(my_pair
.value
, "true", 4) == 0 ||
2242 strncmp(my_pair
.value
, "ON", 2) == 0 || strncmp(my_pair
.value
, "TRUE", 4) == 0 ||
2243 strncmp(my_pair
.value
, "all", 3) == 0 || strncmp(my_pair
.value
, "ALL", 3) == 0) {
2244 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
2245 } else if (strncmp(my_pair
.value
, "off", 3) == 0 || strncmp(my_pair
.value
, "false", 5) == 0 ||
2246 strncmp(my_pair
.value
, "OFF", 3) == 0 || strncmp(my_pair
.value
, "FALSE", 5) == 0 ||
2247 strncmp(my_pair
.value
, "never", 5) == 0 || strncmp(my_pair
.value
, "NEVER", 5) == 5 ||
2248 strncmp(my_pair
.value
, "none", 4) == 0 || strncmp(my_pair
.value
, "NONE", 4) == 0) {
2249 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
2250 } else if (strncmp(my_pair
.value
, "origin", 6) == 0 || strncmp(my_pair
.value
, "ORIGIN", 6) == 0 ||
2251 strncmp(my_pair
.value
, "no_third", 8) == 0 || strncmp(my_pair
.value
, "NO_THIRD", 8) == 0 ||
2252 strncmp(my_pair
.value
, "no third", 8) == 0 || strncmp(my_pair
.value
, "NO THIRD", 8) == 0) {
2253 CookiePolicy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
2257 soup_cookie_jar_set_accept_policy(client
.net
.session_cookie_jar
, CookiePolicy
);
2260 /* SSL certificate checking */
2261 if (strlen(my_pair
.what
) == 9 && strncmp("strictssl", my_pair
.what
, 9) == 0) {
2264 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", TRUE
, NULL
);
2267 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", FALSE
, NULL
);
2270 if (strlen(my_pair
.what
) == 8 && strncmp("cabundle", my_pair
.what
, 8) == 0) {
2271 g_object_set(G_OBJECT(client
.net
.session
), SOUP_SESSION_SSL_CA_FILE
, ca_bundle
, NULL
);
2273 if (strlen(my_pair
.what
) == 10 && strncmp("windowsize", my_pair
.what
, 10) == 0) {
2274 set_default_winsize(my_pair
.value
);
2278 if (browsersettings
[i
].reload
)
2279 webkit_web_view_reload(client
.gui
.webview
);
2287 process_line(char *line
) {
2288 char *c
= line
, *command_hist
;
2290 size_t len
, length
= strlen(line
);
2291 gboolean found
= FALSE
, success
= FALSE
;
2297 /* Ignore blank lines. */
2301 command_hist
= g_strdup(c
);
2303 /* check for colon command aliases first */
2304 for (l
= client
.config
.colon_aliases
; l
; l
= g_list_next(l
)) {
2305 Alias
*alias
= (Alias
*)l
->data
;
2306 if (length
== strlen(alias
->alias
) && strncmp(alias
->alias
, line
, length
) == 0) {
2307 /* reroute to target command */
2309 length
= strlen(alias
->target
);
2314 /* check standard commands */
2315 for (i
= 0; i
< LENGTH(commands
); i
++) {
2316 if (commands
[i
].cmd
== NULL
)
2318 len
= strlen(commands
[i
].cmd
);
2319 if (length
>= len
&& !strncmp(c
, commands
[i
].cmd
, len
) && (c
[len
] == ' ' || !c
[len
])) {
2321 a
.i
= commands
[i
].arg
.i
;
2322 a
.s
= g_strdup(length
> len
+ 1 ? &c
[len
+ 1] : commands
[i
].arg
.s
);
2323 success
= commands
[i
].func(&a
);
2329 save_command_history(command_hist
);
2330 g_free(command_hist
);
2333 echo_message(Error
, "Not a browser command: %s", c
);
2334 } else if (!success
) {
2335 if (client
.state
.error_msg
!= NULL
) {
2336 echo_message(Error
, client
.state
.error_msg
);
2337 g_free(client
.state
.error_msg
);
2338 client
.state
.error_msg
= NULL
;
2340 echo_message(Error
, "Unknown error. Please file a bug report!");
2347 search_tag(const Arg
* a
) {
2349 const char *filename
;
2350 const char *tag
= a
->s
;
2351 char s
[BUFFERSIZE
], foundtag
[40], url
[BUFFERSIZE
];
2355 /* The user must give us something to load up. */
2356 set_error("Bookmark tag required with this option.");
2360 if (strlen(tag
) > MAXTAGSIZE
) {
2361 set_error("Tag too long.");
2365 filename
= g_strdup_printf(BOOKMARKS_STORAGE_FILENAME
);
2366 f
= fopen(filename
, "r");
2367 g_free((gpointer
*)filename
);
2369 set_error("Couldn't open bookmarks file.");
2372 while (fgets(s
, BUFFERSIZE
-1, f
)) {
2375 while (isspace(s
[t
]))
2377 if (s
[t
] != ']') continue;
2390 foundtag
[i
++] = s
[k
++];
2392 /* foundtag now contains the tag */
2393 if (strlen(foundtag
) < MAXTAGSIZE
&& strcmp(tag
, foundtag
) == 0) {
2395 while (isspace(s
[i
])) i
++;
2397 while (s
[i
] && !isspace(s
[i
])) url
[k
++] = s
[i
++];
2399 Arg x
= { .i
= TargetNew
, .s
= url
};
2413 toggle_proxy(gboolean onoff
) {
2415 char *filename
, *new;
2417 if (onoff
== FALSE
) {
2418 g_object_set(client
.net
.session
, "proxy-uri", NULL
, NULL
);
2420 filename
= (char *)g_getenv("http_proxy");
2422 /* Fallthrough to checking HTTP_PROXY as well, since this can also be
2425 if (filename
== NULL
)
2426 filename
= (char *)g_getenv("HTTP_PROXY");
2428 if (filename
!= NULL
&& 0 < strlen(filename
)) {
2429 new = g_strrstr(filename
, "http://") ? g_strdup(filename
) : g_strdup_printf("http://%s", filename
);
2430 proxy_uri
= soup_uri_new(new);
2432 g_object_set(client
.net
.session
, "proxy-uri", proxy_uri
, NULL
);
2434 soup_uri_free(proxy_uri
);
2441 toggle_scrollbars(gboolean onoff
) {
2442 Gui
*gui
= &client
.gui
;
2443 if (onoff
== TRUE
) {
2444 gui
->adjust_h
= gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2445 gui
->adjust_v
= gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gui
->viewport
));
2446 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2448 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2449 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2450 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2452 gtk_widget_set_scroll_adjustments (GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2457 void set_default_winsize(const char * const size
) {
2459 int x
= 640, y
= 480;
2461 x
= strtol(size
, &p
, 10);
2462 if (errno
== ERANGE
|| x
<= 0) {
2467 if (p
== size
|| strlen(size
) == p
- size
)
2470 y
= strtol(p
+ 1, NULL
, 10);
2471 if (errno
== ERANGE
|| y
<= 0)
2475 gtk_window_resize(GTK_WINDOW(client
.gui
.window
), x
, y
);
2479 update_url(const char *uri
) {
2480 Gui
*gui
= &client
.gui
;
2481 gboolean ssl
= g_str_has_prefix(uri
, "https://");
2483 WebKitWebFrame
*frame
;
2484 WebKitWebDataSource
*src
;
2485 WebKitNetworkRequest
*request
;
2488 char *sslactivecolor
;
2490 #ifdef ENABLE_HISTORY_INDICATOR
2491 char before
[] = " [";
2493 gboolean back
= webkit_web_view_can_go_back(gui
->webview
);
2494 gboolean fwd
= webkit_web_view_can_go_forward(gui
->webview
);
2497 before
[0] = after
[0] = '\0';
2499 markup
= g_markup_printf_escaped(
2500 #ifdef ENABLE_HISTORY_INDICATOR
2501 "<span font=\"%s\">%s%s%s%s%s</span>", statusfont
, uri
,
2502 before
, back
? "+" : "", fwd
? "-" : "", after
2504 "<span font=\"%s\">%s</span>", statusfont
, uri
2507 gtk_label_set_markup(GTK_LABEL(gui
->status_url
), markup
);
2510 frame
= webkit_web_view_get_main_frame(gui
->webview
);
2511 src
= webkit_web_frame_get_data_source(frame
);
2512 request
= webkit_web_data_source_get_request(src
);
2513 msg
= webkit_network_request_get_message(request
);
2514 ssl_ok
= soup_message_get_flags(msg
) & SOUP_MESSAGE_CERTIFICATE_TRUSTED
;
2516 sslactivecolor
= sslbgcolor
;
2518 sslactivecolor
= sslinvalidbgcolor
;
2520 gdk_color_parse(ssl
? sslactivecolor
: statusbgcolor
, &color
);
2521 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &color
);
2522 gdk_color_parse(ssl
? sslcolor
: statuscolor
, &color
);
2523 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_url
), GTK_STATE_NORMAL
, &color
);
2524 gtk_widget_modify_fg(GTK_WIDGET(gui
->status_state
), GTK_STATE_NORMAL
, &color
);
2529 State
* state
= &client
.state
;
2531 int download_count
= g_list_length(state
->activeDownloads
);
2532 GString
*status
= g_string_new("");
2534 /* construct the status line */
2536 /* count, modkey and input buffer */
2537 g_string_append_printf(status
, "%.0d", state
->count
);
2538 if (state
->current_modkey
) g_string_append_c(status
, state
->current_modkey
);
2540 /* the number of active downloads */
2541 if (state
->activeDownloads
) {
2542 g_string_append_printf(status
, " %d active %s", download_count
,
2543 (download_count
== 1) ? "download" : "downloads");
2546 #ifdef ENABLE_WGET_PROGRESS_BAR
2547 /* the progressbar */
2550 char progressbar
[progressbartick
+ 1];
2552 if (state
->activeDownloads
) {
2556 for (ptr
= state
->activeDownloads
; ptr
; ptr
= g_list_next(ptr
)) {
2557 progress
+= 100 * webkit_download_get_progress(ptr
->data
);
2560 progress
/= download_count
;
2562 } else if (webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FINISHED
2563 && webkit_web_view_get_load_status(client
.gui
.webview
) != WEBKIT_LOAD_FAILED
) {
2565 progress
= webkit_web_view_get_progress(client
.gui
.webview
) * 100;
2568 if (progress
>= 0) {
2569 ascii_bar(progressbartick
, progress
* progressbartick
/ 100, progressbar
);
2570 g_string_append_printf(status
, " %c%s%c",
2571 progressborderleft
, progressbar
, progressborderright
);
2576 /* and the current scroll position */
2578 int max
= gtk_adjustment_get_upper(client
.gui
.adjust_v
) - gtk_adjustment_get_page_size(client
.gui
.adjust_v
);
2579 int val
= (int)(gtk_adjustment_get_value(client
.gui
.adjust_v
) / max
* 100);
2582 g_string_append(status
, " All");
2584 g_string_append(status
, " Top");
2585 else if (val
== 100)
2586 g_string_append(status
, " Bot");
2588 g_string_append_printf(status
, " %d%%", val
);
2592 markup
= g_markup_printf_escaped("<span font=\"%s\">%s</span>", statusfont
, status
->str
);
2593 gtk_label_set_markup(GTK_LABEL(client
.gui
.status_state
), markup
);
2596 g_string_free(status
, TRUE
);
2600 setup_client(void) {
2601 State
*state
= &client
.state
;
2602 Config
*config
= &client
.config
;
2604 state
->mode
= ModeNormal
;
2606 state
->rememberedURI
[0] = '\0';
2607 state
->manual_focus
= FALSE
;
2608 state
->is_inspecting
= FALSE
;
2609 state
->commandhistory
= NULL
;
2610 state
->commandpointer
= 0;
2612 config
->colon_aliases
= NULL
;
2613 config
->cookie_timeout
= 4800;
2619 client
.config
.modkeys
= calloc(LENGTH(keys
) + 1, sizeof(char));
2620 char *ptr
= client
.config
.modkeys
;
2622 for (i
= 0; i
< LENGTH(keys
); i
++)
2623 if (keys
[i
].modkey
&& !strchr(client
.config
.modkeys
, keys
[i
].modkey
))
2624 *(ptr
++) = keys
[i
].modkey
;
2625 client
.config
.modkeys
= realloc(client
.config
.modkeys
, &ptr
[0] - &client
.config
.modkeys
[0] + 1);
2630 Gui
*gui
= &client
.gui
;
2631 State
*state
= &client
.state
;
2633 gui
->scroll_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
2634 gui
->scroll_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
2635 gui
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_h
));
2636 gui
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(gui
->scroll_v
));
2637 if (client
.state
.embed
) {
2638 gui
->window
= GTK_WINDOW(gtk_plug_new(client
.state
.embed
));
2640 gui
->window
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL
));
2641 gtk_window_set_wmclass(GTK_WINDOW(gui
->window
), "vimprobable2", "Vimprobable2");
2643 gtk_window_set_default_size(GTK_WINDOW(gui
->window
), 640, 480);
2644 gui
->box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
2645 gui
->inputbox
= gtk_entry_new();
2646 gui
->webview
= (WebKitWebView
*)webkit_web_view_new();
2647 gui
->statusbar
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2648 gui
->eventbox
= gtk_event_box_new();
2649 gui
->status_url
= gtk_label_new(NULL
);
2650 gui
->status_state
= gtk_label_new(NULL
);
2652 PangoFontDescription
*font
;
2653 GdkGeometry hints
= { 1, 1 };
2654 gui
->inspector
= webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(gui
->webview
));
2656 state
->clipboards
[0] = gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2657 state
->clipboards
[1] = gtk_clipboard_get(GDK_NONE
);
2659 gdk_color_parse(statusbgcolor
, &bg
);
2660 gtk_widget_modify_bg(gui
->eventbox
, GTK_STATE_NORMAL
, &bg
);
2661 gtk_widget_set_name(GTK_WIDGET(gui
->window
), "Vimprobable2");
2662 gtk_window_set_geometry_hints(gui
->window
, NULL
, &hints
, GDK_HINT_MIN_SIZE
);
2664 state
->keymap
= gdk_keymap_get_default();
2666 #ifdef DISABLE_SCROLLBAR
2667 gui
->viewport
= gtk_scrolled_window_new(NULL
, NULL
);
2668 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui
->viewport
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
2670 /* Ensure we still see scrollbars. */
2671 gui
->viewport
= gtk_scrolled_window_new(gui
->adjust_h
, gui
->adjust_v
);
2674 gui
->pane
= gtk_vpaned_new();
2675 gtk_paned_pack1(GTK_PANED(gui
->pane
), GTK_WIDGET(gui
->box
), TRUE
, TRUE
);
2678 gtk_container_add(GTK_CONTAINER(gui
->viewport
), GTK_WIDGET(gui
->webview
));
2680 /* Ensure we set the scroll adjustments now, so that if we're not drawing
2681 * titlebars, we can still scroll.
2683 gtk_widget_set_scroll_adjustments(GTK_WIDGET(gui
->webview
), gui
->adjust_h
, gui
->adjust_v
);
2685 font
= pango_font_description_from_string(urlboxfont
[0]);
2686 gtk_widget_modify_font(GTK_WIDGET(gui
->inputbox
), font
);
2687 pango_font_description_free(font
);
2688 gtk_entry_set_inner_border(GTK_ENTRY(gui
->inputbox
), NULL
);
2689 gtk_misc_set_alignment(GTK_MISC(gui
->status_url
), 0.0, 0.0);
2690 gtk_misc_set_alignment(GTK_MISC(gui
->status_state
), 1.0, 0.0);
2691 gtk_box_pack_start(gui
->statusbar
, gui
->status_url
, TRUE
, TRUE
, 2);
2692 gtk_box_pack_start(gui
->statusbar
, gui
->status_state
, FALSE
, FALSE
, 2);
2693 gtk_container_add(GTK_CONTAINER(gui
->eventbox
), GTK_WIDGET(gui
->statusbar
));
2694 gtk_box_pack_start(gui
->box
, gui
->viewport
, TRUE
, TRUE
, 0);
2695 gtk_box_pack_start(gui
->box
, gui
->eventbox
, FALSE
, FALSE
, 0);
2696 gtk_entry_set_has_frame(GTK_ENTRY(gui
->inputbox
), FALSE
);
2697 gtk_box_pack_end(gui
->box
, gui
->inputbox
, FALSE
, FALSE
, 0);
2698 gtk_container_add(GTK_CONTAINER(gui
->window
), GTK_WIDGET(gui
->pane
));
2699 gtk_widget_grab_focus(GTK_WIDGET(gui
->webview
));
2700 gtk_widget_show_all(GTK_WIDGET(gui
->window
));
2701 set_widget_font_and_color(gui
->inputbox
, urlboxfont
[0], urlboxbgcolor
[0], urlboxcolor
[0]);
2702 g_object_set(gtk_widget_get_settings(gui
->inputbox
), "gtk-entry-select-on-focus", FALSE
, NULL
);
2707 WebKitWebSettings
*settings
= (WebKitWebSettings
*)webkit_web_settings_new();
2708 char *filename
, *file_url
;
2710 client
.net
.session
= webkit_get_default_session();
2711 g_object_set(G_OBJECT(client
.net
.session
), "ssl-ca-file", ca_bundle
, NULL
);
2712 g_object_set(G_OBJECT(client
.net
.session
), "ssl-strict", strict_ssl
, NULL
);
2713 g_object_set(G_OBJECT(settings
), "default-font-size", DEFAULT_FONT_SIZE
, NULL
);
2714 g_object_set(G_OBJECT(settings
), "enable-scripts", enablePlugins
, NULL
);
2715 g_object_set(G_OBJECT(settings
), "enable-plugins", enablePlugins
, NULL
);
2716 g_object_set(G_OBJECT(settings
), "enable-java-applet", enableJava
, NULL
);
2717 g_object_set(G_OBJECT(settings
), "enable-page-cache", enablePagecache
, NULL
);
2718 g_object_set(G_OBJECT(settings
), "enable-html5-local-storage", enableLocalstorage
, NULL
);
2719 g_object_set(G_OBJECT(settings
), "enable-html5-database", enableDatabase
, NULL
);
2720 g_object_set(G_OBJECT(settings
), "javascript-can-open-windows-automatically", javascriptPopups
, NULL
);
2721 filename
= g_strdup_printf(USER_STYLESHEET
);
2722 file_url
= g_strdup_printf("file://%s", filename
);
2723 g_object_set(G_OBJECT(settings
), "user-stylesheet-uri", file_url
, NULL
);
2726 g_object_set(G_OBJECT(settings
), "user-agent", useragent
, NULL
);
2727 g_object_get(G_OBJECT(settings
), "zoom-step", &client
.config
.zoomstep
, NULL
);
2728 webkit_web_view_set_settings(client
.gui
.webview
, settings
);
2731 toggle_proxy(use_proxy
);
2736 WebKitWebFrame
*frame
= webkit_web_view_get_main_frame(client
.gui
.webview
);
2737 #ifdef ENABLE_COOKIE_SUPPORT
2739 g_signal_connect_after(G_OBJECT(client
.net
.session
), "request-started", G_CALLBACK(new_generic_request
), NULL
);
2741 /* Accept-language header */
2742 g_object_set(G_OBJECT(client
.net
.session
), "accept-language", acceptlanguage
, NULL
);
2744 g_object_connect(G_OBJECT(client
.gui
.window
),
2745 "signal::destroy", G_CALLBACK(window_destroyed_cb
), NULL
,
2748 g_signal_connect(G_OBJECT(frame
),
2749 "scrollbars-policy-changed", G_CALLBACK(blank_cb
), NULL
);
2751 g_object_connect(G_OBJECT(client
.gui
.webview
),
2752 "signal::title-changed", G_CALLBACK(webview_title_changed_cb
), NULL
,
2753 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), NULL
,
2754 "signal::load-committed", G_CALLBACK(webview_load_committed_cb
), NULL
,
2755 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), NULL
,
2756 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_navigation_cb
), NULL
,
2757 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_new_window_cb
), NULL
,
2758 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), NULL
,
2759 "signal::download-requested", G_CALLBACK(webview_download_cb
), NULL
,
2760 "signal::key-press-event", G_CALLBACK(webview_keypress_cb
), NULL
,
2761 "signal::hovering-over-link", G_CALLBACK(webview_hoverlink_cb
), NULL
,
2762 "signal::console-message", G_CALLBACK(webview_console_cb
), NULL
,
2763 "signal::create-web-view", G_CALLBACK(webview_open_in_new_window_cb
), NULL
,
2764 "signal::event", G_CALLBACK(notify_event_cb
), NULL
,
2766 /* webview adjustment */
2767 g_object_connect(G_OBJECT(client
.gui
.adjust_v
),
2768 "signal::value-changed", G_CALLBACK(webview_scroll_cb
), NULL
,
2771 g_object_connect(G_OBJECT(client
.gui
.inputbox
),
2772 "signal::activate", G_CALLBACK(inputbox_activate_cb
), NULL
,
2773 "signal::key-press-event", G_CALLBACK(inputbox_keypress_cb
), NULL
,
2774 "signal::key-release-event", G_CALLBACK(inputbox_keyrelease_cb
), NULL
,
2775 "signal::changed", G_CALLBACK(inputbox_changed_cb
), NULL
,
2778 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2779 "inspect-web-view", G_CALLBACK(inspector_new_cb
), NULL
);
2780 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2781 "show-window", G_CALLBACK(inspector_show_cb
), NULL
);
2782 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2783 "close-window", G_CALLBACK(inspector_close_cb
), NULL
);
2784 g_signal_connect(G_OBJECT(client
.gui
.inspector
),
2785 "finished", G_CALLBACK(inspector_finished_cb
), NULL
);
2788 #ifdef ENABLE_USER_SCRIPTFILE
2790 scripts_run_user_file() {
2791 gchar
*js
= NULL
, *user_scriptfile
= NULL
;
2792 GError
*error
= NULL
;
2794 user_scriptfile
= g_strdup_printf(USER_SCRIPTFILE
);
2796 /* run the users script file */
2797 if (g_file_test(user_scriptfile
, G_FILE_TEST_IS_REGULAR
)
2798 && g_file_get_contents(user_scriptfile
, &js
, NULL
, &error
)) {
2800 gchar
*value
= NULL
, *message
= NULL
;
2802 jsapi_evaluate_script(js
, &value
, &message
);
2805 fprintf(stderr
, "%s", message
);
2810 fprintf(stderr
, "Cannot open %s: %s\n", user_scriptfile
, error
? error
->message
: "file not found");
2813 g_free(user_scriptfile
);
2817 #ifdef ENABLE_COOKIE_SUPPORT
2821 Network
*net
= &client
.net
;
2822 if (net
->file_cookie_jar
)
2823 g_object_unref(net
->file_cookie_jar
);
2825 if (net
->session_cookie_jar
)
2826 g_object_unref(net
->session_cookie_jar
);
2828 net
->session_cookie_jar
= soup_cookie_jar_new();
2829 soup_cookie_jar_set_accept_policy(net
->session_cookie_jar
, CookiePolicy
);
2831 net
->cookie_store
= g_strdup_printf(COOKIES_STORAGE_FILENAME
);
2835 g_signal_connect(G_OBJECT(net
->file_cookie_jar
), "changed",
2836 G_CALLBACK(update_cookie_jar
), NULL
);
2839 /* This function could be used for any header requests we receive
2840 * for not, it's limited to handling cookies
2843 new_generic_request(SoupSession
*session
, SoupMessage
*soup_msg
, gpointer unused
)
2845 SoupMessageHeaders
*soup_msg_h
;
2849 soup_msg_h
= soup_msg
->request_headers
;
2850 soup_message_headers_remove(soup_msg_h
, "Cookie");
2851 uri
= soup_message_get_uri(soup_msg
);
2852 soup_message_set_first_party(soup_msg
, uri
);
2853 if ((cookie_str
= get_cookies(uri
))) {
2854 soup_message_headers_append(soup_msg_h
, "Cookie", cookie_str
);
2858 g_signal_connect_after(G_OBJECT(soup_msg
), "got-headers", G_CALLBACK(handle_response_headers
), NULL
);
2864 get_cookies(SoupURI
*soup_uri
) {
2867 cookie_str
= soup_cookie_jar_get_cookies(client
.net
.file_cookie_jar
, soup_uri
, TRUE
);
2873 handle_response_headers(SoupMessage
*soup_msg
, gpointer unused
)
2875 GSList
*resp_cookie
= NULL
, *cookie_list
;
2877 SoupURI
*uri
= soup_message_get_uri(soup_msg
);
2879 if (CookiePolicy
!= SOUP_COOKIE_JAR_ACCEPT_NEVER
) {
2880 cookie_list
= soup_cookies_from_response(soup_msg
);
2881 for(resp_cookie
= cookie_list
; resp_cookie
; resp_cookie
= g_slist_next(resp_cookie
))
2883 SoupDate
*soup_date
;
2884 cookie
= soup_cookie_copy((SoupCookie
*)resp_cookie
->data
);
2886 if (client
.config
.cookie_timeout
&& cookie
->expires
== NULL
) {
2887 soup_date
= soup_date_new_from_time_t(time(NULL
) + client
.config
.cookie_timeout
* 10);
2888 soup_cookie_set_expires(cookie
, soup_date
);
2889 soup_date_free(soup_date
);
2891 if (CookiePolicy
!= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
) {
2892 /* no third party cookies: for libsoup 2.4 and later, the following should work */
2893 /*soup_cookie_jar_add_cookie_with_first_party(client.net.file_cookie_jar, uri, cookie);*/
2894 if (strcmp(soup_uri_get_host(uri
), soup_cookie_get_domain(cookie
)) == 0) {
2895 soup_cookie_jar_add_cookie(client
.net
.file_cookie_jar
, cookie
);
2898 soup_cookie_jar_add_cookie(client
.net
.file_cookie_jar
, cookie
);
2902 soup_cookies_free(cookie_list
);
2909 update_cookie_jar(SoupCookieJar
*jar
, SoupCookie
*old
, SoupCookie
*new)
2912 /* Nothing to do. */
2916 if (CookiePolicy
!= SOUP_COOKIE_JAR_ACCEPT_NEVER
) {
2918 copy
= soup_cookie_copy(new);
2920 soup_cookie_jar_add_cookie(client
.net
.session_cookie_jar
, copy
);
2927 load_all_cookies(void)
2929 Network
*net
= &client
.net
;
2930 GSList
*cookie_list
;
2931 net
->file_cookie_jar
= soup_cookie_jar_text_new(net
->cookie_store
, COOKIES_STORAGE_READONLY
);
2933 /* Put them back in the session store. */
2934 GSList
*cookies_from_file
= soup_cookie_jar_all_cookies(net
->file_cookie_jar
);
2935 cookie_list
= cookies_from_file
;
2937 for (; cookies_from_file
;
2938 cookies_from_file
= cookies_from_file
->next
)
2940 soup_cookie_jar_add_cookie(net
->session_cookie_jar
, cookies_from_file
->data
);
2943 soup_cookies_free(cookies_from_file
);
2944 g_slist_free(cookie_list
);
2953 /* Free up any nasty globals before exiting. */
2954 #ifdef ENABLE_COOKIE_SUPPORT
2955 if (client
.net
.cookie_store
)
2956 g_free(client
.net
.cookie_store
);
2962 main(int argc
, char *argv
[]) {
2964 static char url
[256] = "";
2965 static gboolean ver
= false;
2966 static gboolean configfile_exists
= FALSE
;
2967 static const char *cfile
= NULL
;
2968 static gchar
*winid
= NULL
;
2969 static GOptionEntry opts
[] = {
2970 { "version", 'v', 0, G_OPTION_ARG_NONE
, &ver
, "print version", NULL
},
2971 { "embed", 'e', 0, G_OPTION_ARG_STRING
, &winid
, "embedded", NULL
},
2972 { "configfile", 'c', 0, G_OPTION_ARG_STRING
, &cfile
, "config file", NULL
},
2977 Config
*config
= &client
.config
;
2979 /* command line argument: version */
2980 if (!gtk_init_with_args(&argc
, &argv
, "[<uri>]", opts
, NULL
, &err
)) {
2981 g_printerr("can't init gtk: %s\n", err
->message
);
2983 return EXIT_FAILURE
;
2987 printf("%s\n", INTERNAL_VERSION
);
2988 return EXIT_SUCCESS
;
2993 if (getenv("TMPDIR")) {
2994 strncpy(temp_dir
, getenv("TMPDIR"), MAX_SETTING_SIZE
);
2995 temp_dir
[MAX_SETTING_SIZE
-1] = 0;
2998 if( getenv("XDG_CONFIG_HOME") )
2999 config
->config_base
= g_strdup_printf("%s", getenv("XDG_CONFIG_HOME"));
3001 config
->config_base
= g_strdup_printf("%s/.config/", getenv("HOME"));
3003 sprintf(downloads_path
, "%s", getenv("HOME"));
3006 config
->configfile
= g_strdup(cfile
);
3008 config
->configfile
= g_strdup_printf(RCFILE
);
3010 if (!g_thread_supported())
3011 g_thread_init(NULL
);
3014 if (strncmp(winid
, "0x", 2) == 0) {
3015 client
.state
.embed
= strtol(winid
, NULL
, 16);
3017 client
.state
.embed
= atoi(winid
);
3024 #ifdef ENABLE_COOKIE_SUPPORT
3028 make_searchengines_list(searchengines
, LENGTH(searchengines
));
3029 make_uri_handlers_list(uri_handlers
, LENGTH(uri_handlers
));
3031 /* Check if the specified file exists. */
3032 /* And only warn the user, if they explicitly asked for a config on the
3035 if (!(access(config
->configfile
, F_OK
) == 0) && cfile
) {
3036 echo_message(Info
, "Config file '%s' doesn't exist", cfile
);
3037 } else if ((access(config
->configfile
, F_OK
) == 0))
3038 configfile_exists
= true;
3040 /* read config file */
3041 /* But only report errors if we failed, and the file existed. */
3042 if ((SUCCESS
!= read_rcfile(config
->configfile
)) && configfile_exists
) {
3043 echo_message(Error
, "Error in config file '%s'", config
->configfile
);
3044 g_free(config
->configfile
);
3047 /* command line argument: URL */
3049 strncpy(url
, argv
[argc
- 1], 255);
3051 strncpy(url
, startpage
, 255);
3054 a
.i
= TargetCurrent
;
3061 return EXIT_SUCCESS
;