2 * vimb - a webkit based vim like browser.
4 * Copyright (C) 2012-2013 Daniel Carl
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://www.gnu.org/licenses/.
27 #include "completion.h"
47 static void webview_progress_cb(WebKitWebView *view, GParamSpec *pspec);
48 static void webview_download_progress_cb(WebKitWebView *view, GParamSpec *pspec);
49 static void webview_load_status_cb(WebKitWebView *view, GParamSpec *pspec);
50 static void destroy_window_cb(GtkWidget *widget);
51 static void scroll_cb(GtkAdjustment *adjustment);
52 static WebKitWebView *inspector_new(WebKitWebInspector *inspector, WebKitWebView *webview);
53 static gboolean inspector_show(WebKitWebInspector *inspector);
54 static gboolean inspector_close(WebKitWebInspector *inspector);
55 static void inspector_finished(WebKitWebInspector *inspector);
56 static gboolean button_relase_cb(WebKitWebView *webview, GdkEventButton *event);
57 static gboolean new_window_policy_cb(
58 WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *request,
59 WebKitWebNavigationAction *navig, WebKitWebPolicyDecision *policy);
60 static WebKitWebView *create_web_view_cb(WebKitWebView *view, WebKitWebFrame *frame);
61 static void create_web_view_received_uri_cb(WebKitWebView *view);
62 static void hover_link_cb(WebKitWebView *webview, const char *title, const char *link);
63 static void title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, const char *title);
64 static gboolean mimetype_decision_cb(WebKitWebView *webview,
65 WebKitWebFrame *frame, WebKitNetworkRequest *request, char*
66 mime_type, WebKitWebPolicyDecision *decision);
67 static void download_progress_cp(WebKitDownload *download, GParamSpec *pspec);
70 static void update_title(void);
71 static void run_user_script(WebKitWebFrame *frame);
72 static char *jsref_to_string(JSContextRef context, JSValueRef ref);
73 static void init_core(void);
74 static void read_config(void);
75 static void setup_signals();
76 static void init_files(void);
77 static gboolean hide_message();
78 static void set_status(const StatusType status);
79 static void input_print(gboolean force, const MessageType type, gboolean hide, const char *message);
81 void vb_echo_force(const MessageType type, gboolean hide, const char *error, ...)
86 va_start(args, error);
87 buffer = g_strdup_vprintf(error, args);
90 input_print(true, type, hide, buffer);
94 void vb_echo(const MessageType type, gboolean hide, const char *error, ...)
99 va_start(args, error);
100 buffer = g_strdup_vprintf(error, args);
103 input_print(false, type, hide, buffer);
107 static void input_print(gboolean force, const MessageType type, gboolean hide,
110 /* don't print message if the input is focussed */
111 if (!force && gtk_widget_is_focus(GTK_WIDGET(vb.gui.input))) {
115 /* apply input style only if the message type was changed */
116 if (type != vb.state.input_type) {
117 vb.state.input_type = type;
118 vb_update_input_style();
120 vb_set_input_text(message);
122 g_timeout_add_seconds(MESSAGE_TIMEOUT, (GSourceFunc)hide_message, NULL);
127 * Writes given text into the command line.
129 void vb_set_input_text(const char *text)
131 gtk_text_buffer_set_text(vb.gui.buffer, text, -1);
135 * Retrieves the content of the command line.
136 * Retruned string must be freed with g_free.
138 char *vb_get_input_text(void)
140 GtkTextIter start, end;
142 gtk_text_buffer_get_bounds(vb.gui.buffer, &start, &end);
143 return gtk_text_buffer_get_text(vb.gui.buffer, &start, &end, false);
146 gboolean vb_eval_script(WebKitWebFrame *frame, char *script, char *file, char **value)
148 JSStringRef str, file_name;
149 JSValueRef exception = NULL, result = NULL;
152 js = webkit_web_frame_get_global_context(frame);
153 str = JSStringCreateWithUTF8CString(script);
154 file_name = JSStringCreateWithUTF8CString(file);
156 result = JSEvaluateScript(js, str, JSContextGetGlobalObject(js), file_name, 0, &exception);
157 JSStringRelease(file_name);
158 JSStringRelease(str);
161 *value = jsref_to_string(js, result);
165 *value = jsref_to_string(js, exception);
169 gboolean vb_load_uri(const Arg *arg)
171 char *uri = NULL, *rp, *path = NULL;
175 path = g_strstrip(arg->s);
177 if (!path || !*path) {
178 path = vb.config.home_page;
181 if (strstr(path, "://") || !strncmp(path, "about:", 6)) {
182 uri = g_strdup(path);
183 } else if (stat(path, &st) == 0) {
184 /* check if the path is a file path */
185 rp = realpath(path, NULL);
186 uri = g_strconcat("file://", rp, NULL);
188 } else if (strchr(path, ' ') || !strchr(path, '.')) {
189 /* use a shortcut if path contains spaces or no dot */
190 uri = shortcut_get_uri(path);
194 uri = g_strconcat("http://", path, NULL);
197 if (arg->i == VB_TARGET_NEW) {
199 char *cmd[7], xid[64];
204 snprintf(xid, LENGTH(xid), "%u", (int)vb.embed);
207 if (vb.custom_config) {
209 cmd[i++] = vb.custom_config;
214 /* spawn a new browser instance */
215 g_spawn_async(NULL, cmd, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
217 /* Load a web page into the browser instance */
218 webkit_web_view_load_uri(vb.gui.webview, uri);
219 /* show the url to be opened in the windo title until we receive the
221 OVERWRITE_STRING(vb.state.title, uri);
229 gboolean vb_set_clipboard(const Arg *arg)
231 gboolean result = false;
236 if (arg->i & VB_CLIPBOARD_PRIMARY) {
237 gtk_clipboard_set_text(PRIMARY_CLIPBOARD(), arg->s, -1);
240 if (arg->i & VB_CLIPBOARD_SECONDARY) {
241 gtk_clipboard_set_text(SECONDARY_CLIPBOARD(), arg->s, -1);
248 void vb_set_widget_font(GtkWidget *widget, const VbColor *fg, const VbColor *bg, PangoFontDescription *font)
250 VB_WIDGET_OVERRIDE_FONT(widget, font);
251 VB_WIDGET_OVERRIDE_TEXT(widget, VB_GTK_STATE_NORMAL, fg);
252 VB_WIDGET_OVERRIDE_COLOR(widget, VB_GTK_STATE_NORMAL, fg);
253 VB_WIDGET_OVERRIDE_BASE(widget, VB_GTK_STATE_NORMAL, bg);
254 VB_WIDGET_OVERRIDE_BACKGROUND(widget, VB_GTK_STATE_NORMAL, bg);
257 void vb_update_statusbar()
260 GString *status = g_string_new("");
262 /* show the active downloads */
263 if (vb.state.downloads) {
264 num = g_list_length(vb.state.downloads);
265 g_string_append_printf(status, " %d %s", num, num == 1 ? "download" : "downloads");
268 /* show load status of page or the downloads */
269 if (vb.state.progress != 100) {
270 g_string_append_printf(status, " [%i%%]", vb.state.progress);
273 /* show the scroll status */
274 max = gtk_adjustment_get_upper(vb.gui.adjust_v) - gtk_adjustment_get_page_size(vb.gui.adjust_v);
275 val = (int)floor(0.5 + (gtk_adjustment_get_value(vb.gui.adjust_v) / max * 100));
278 g_string_append(status, " All");
279 } else if (val == 0) {
280 g_string_append(status, " Top");
281 } else if (val >= 100) {
282 g_string_append(status, " Bot");
284 g_string_append_printf(status, " %d%%", val);
287 gtk_label_set_text(GTK_LABEL(vb.gui.statusbar.right), status->str);
288 g_string_free(status, true);
291 void vb_update_status_style(void)
293 StatusType type = vb.state.status_type;
295 vb.gui.eventbox, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
298 vb.gui.statusbar.left, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
301 vb.gui.statusbar.right, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
304 vb.gui.statusbar.cmd, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
308 void vb_update_input_style(void)
310 MessageType type = vb.state.input_type;
312 vb.gui.input, &vb.style.input_fg[type], &vb.style.input_bg[type], vb.style.input_font[type]
316 void vb_update_urlbar(const char *uri)
319 #ifdef FEATURE_HISTORY_INDICATOR
323 back = webkit_web_view_can_go_back(gui->webview);
324 fwd = webkit_web_view_can_go_forward(gui->webview);
326 /* show history indicator only if there is something to show */
328 str = g_strdup_printf("%s [%s]", uri, back ? (fwd ? "-+" : "-") : "+");
329 gtk_label_set_text(GTK_LABEL(gui->statusbar.left), str);
332 gtk_label_set_text(GTK_LABEL(gui->statusbar.left), uri);
335 gtk_label_set_text(GTK_LABEL(gui->statusbar.left), uri);
341 const char *uri = GET_URI();
342 /* write last URL into file for recreation */
344 g_file_set_contents(vb.files[FILES_CLOSED], uri, -1, NULL);
349 webkit_web_view_stop_loading(vb.gui.webview);
357 for (int i = 0; i < FILES_LAST; i++) {
364 static gboolean hide_message()
366 input_print(false, VB_MSG_NORMAL, false, "");
371 static void webview_progress_cb(WebKitWebView *view, GParamSpec *pspec)
373 vb.state.progress = webkit_web_view_get_progress(view) * 100;
374 vb_update_statusbar();
378 static void webview_download_progress_cb(WebKitWebView *view, GParamSpec *pspec)
380 if (vb.state.downloads) {
381 vb.state.progress = 0;
383 for (ptr = vb.state.downloads; ptr; ptr = g_list_next(ptr)) {
384 vb.state.progress += 100 * webkit_download_get_progress(ptr->data);
386 vb.state.progress /= g_list_length(vb.state.downloads);
388 vb_update_statusbar();
392 static void webview_load_status_cb(WebKitWebView *view, GParamSpec *pspec)
394 const char *uri = GET_URI();
396 switch (webkit_web_view_get_load_status(vb.gui.webview)) {
397 case WEBKIT_LOAD_PROVISIONAL:
398 /* update load progress in statusbar */
399 vb.state.progress = 0;
400 vb_update_statusbar();
404 case WEBKIT_LOAD_COMMITTED:
406 WebKitWebFrame *frame = webkit_web_view_get_main_frame(vb.gui.webview);
408 if (g_str_has_prefix(uri, "https://")) {
409 WebKitWebDataSource *src = webkit_web_frame_get_data_source(frame);
410 WebKitNetworkRequest *request = webkit_web_data_source_get_request(src);
411 SoupMessage *msg = webkit_network_request_get_message(request);
412 SoupMessageFlags flags = soup_message_get_flags(msg);
414 (flags & SOUP_MESSAGE_CERTIFICATE_TRUSTED) ? VB_STATUS_SSL_VALID : VB_STATUS_SSL_INVALID
417 set_status(VB_STATUS_NORMAL);
420 /* inject the hinting javascript */
423 /* run user script file */
424 run_user_script(frame);
427 /* if we load a page from a submitted form, leafe the insert mode */
428 if (vb.mode->id == 'i') {
432 vb_update_statusbar();
433 vb_update_urlbar(uri);
437 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
440 case WEBKIT_LOAD_FINISHED:
441 /* update load progress in statusbar */
442 vb.state.progress = 100;
443 vb_update_statusbar();
446 if (strncmp(uri, "about:", 6)) {
447 dom_check_auto_insert(view);
448 history_add(HISTORY_URL, uri, webkit_web_view_get_title(view));
452 case WEBKIT_LOAD_FAILED:
457 static void destroy_window_cb(GtkWidget *widget)
462 static void scroll_cb(GtkAdjustment *adjustment)
464 vb_update_statusbar();
467 static WebKitWebView *inspector_new(WebKitWebInspector *inspector, WebKitWebView *webview)
469 return WEBKIT_WEB_VIEW(webkit_web_view_new());
472 static gboolean inspector_show(WebKitWebInspector *inspector)
474 WebKitWebView *webview;
477 if (vb.state.is_inspecting) {
481 webview = webkit_web_inspector_get_web_view(inspector);
483 /* use about 1/3 of window height for the inspector */
484 gtk_window_get_size(GTK_WINDOW(vb.gui.window), NULL, &height);
485 gtk_paned_set_position(GTK_PANED(vb.gui.pane), 2 * height / 3);
487 gtk_paned_pack2(GTK_PANED(vb.gui.pane), GTK_WIDGET(webview), true, true);
488 gtk_widget_show(GTK_WIDGET(webview));
490 vb.state.is_inspecting = true;
495 static gboolean inspector_close(WebKitWebInspector *inspector)
497 WebKitWebView *webview;
499 if (!vb.state.is_inspecting) {
502 webview = webkit_web_inspector_get_web_view(inspector);
503 gtk_widget_hide(GTK_WIDGET(webview));
504 gtk_widget_destroy(GTK_WIDGET(webview));
506 vb.state.is_inspecting = false;
511 static void inspector_finished(WebKitWebInspector *inspector)
513 g_free(vb.gui.inspector);
516 static void set_status(const StatusType status)
518 if (vb.state.status_type != status) {
519 vb.state.status_type = status;
520 /* update the statusbar style only if the status changed */
521 vb_update_status_style();
525 static void run_user_script(WebKitWebFrame *frame)
527 char *js = NULL, *value = NULL;
528 GError *error = NULL;
530 if (g_file_test(vb.files[FILES_SCRIPT], G_FILE_TEST_IS_REGULAR)
531 && g_file_get_contents(vb.files[FILES_SCRIPT], &js, NULL, &error)
533 gboolean success = vb_eval_script(frame, js, vb.files[FILES_SCRIPT], &value);
535 fprintf(stderr, "%s", value);
542 static char *jsref_to_string(JSContextRef context, JSValueRef ref)
545 JSStringRef str_ref = JSValueToStringCopy(context, ref, NULL);
546 size_t len = JSStringGetMaximumUTF8CStringSize(str_ref);
548 string = g_new0(char, len);
549 JSStringGetUTF8CString(str_ref, string, len);
550 JSStringRelease(str_ref);
555 static void init_core(void)
560 gui->window = gtk_plug_new(vb.embed);
562 gui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
564 gtk_window_set_has_resize_grip(GTK_WINDOW(gui->window), false);
566 gtk_window_set_wmclass(GTK_WINDOW(gui->window), PROJECT, PROJECT_UCFIRST);
567 gtk_window_set_role(GTK_WINDOW(gui->window), PROJECT_UCFIRST);
570 GdkGeometry hints = {10, 10};
571 gtk_window_set_default_size(GTK_WINDOW(gui->window), WIN_WIDTH, WIN_HEIGHT);
572 gtk_window_set_title(GTK_WINDOW(gui->window), PROJECT "/" VERSION);
573 gtk_window_set_geometry_hints(GTK_WINDOW(gui->window), NULL, &hints, GDK_HINT_MIN_SIZE);
574 gtk_window_set_icon(GTK_WINDOW(gui->window), NULL);
575 gtk_widget_set_name(GTK_WIDGET(gui->window), PROJECT);
577 /* Create a browser instance */
578 gui->webview = WEBKIT_WEB_VIEW(webkit_web_view_new());
579 gui->inspector = webkit_web_view_get_inspector(gui->webview);
581 /* Create a scrollable area */
582 gui->scroll = gtk_scrolled_window_new(NULL, NULL);
583 gui->adjust_h = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(gui->scroll));
584 gui->adjust_v = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gui->scroll));
586 #ifdef FEATURE_NO_SCROLLBARS
588 /* set the default style for the application - this can be overwritten by
589 * the users style in gtk-3.0/gtk.css */
590 char *style = "GtkScrollbar{-GtkRange-slider-width:0;-GtkRange-trough-border:0;}";
591 GtkCssProvider *provider = gtk_css_provider_get_default();
592 gtk_css_provider_load_from_data(provider, style, -1, NULL);
593 gtk_style_context_add_provider_for_screen(
594 gdk_screen_get_default(),
595 GTK_STYLE_PROVIDER(provider),
596 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
599 /* GTK_POLICY_NEVER with gtk3 disallows window resizing and scrolling */
600 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gui->scroll), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
604 /* Prepare the command line */
605 gui->input = gtk_text_view_new();
606 gui->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gui->input));
609 gui->pane = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
610 gui->box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
611 gui->statusbar.box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
613 gui->pane = gtk_vpaned_new();
614 gui->box = GTK_BOX(gtk_vbox_new(false, 0));
615 gui->statusbar.box = GTK_BOX(gtk_hbox_new(false, 0));
617 gui->statusbar.left = gtk_label_new(NULL);
618 gui->statusbar.right = gtk_label_new(NULL);
619 gui->statusbar.cmd = gtk_label_new(NULL);
621 /* Prepare the event box */
622 gui->eventbox = gtk_event_box_new();
624 gtk_paned_pack1(GTK_PANED(gui->pane), GTK_WIDGET(gui->box), true, true);
626 /* Put all part together */
627 gtk_container_add(GTK_CONTAINER(gui->scroll), GTK_WIDGET(gui->webview));
628 gtk_container_add(GTK_CONTAINER(gui->eventbox), GTK_WIDGET(gui->statusbar.box));
629 gtk_container_add(GTK_CONTAINER(gui->window), GTK_WIDGET(gui->pane));
630 gtk_misc_set_alignment(GTK_MISC(gui->statusbar.left), 0.0, 0.0);
631 gtk_misc_set_alignment(GTK_MISC(gui->statusbar.right), 1.0, 0.0);
632 gtk_misc_set_alignment(GTK_MISC(gui->statusbar.cmd), 1.0, 0.0);
633 gtk_box_pack_start(gui->statusbar.box, gui->statusbar.left, true, true, 2);
634 gtk_box_pack_start(gui->statusbar.box, gui->statusbar.cmd, false, false, 0);
635 gtk_box_pack_start(gui->statusbar.box, gui->statusbar.right, false, false, 2);
637 gtk_box_pack_start(gui->box, gui->scroll, true, true, 0);
638 gtk_box_pack_start(gui->box, gui->eventbox, false, false, 0);
639 gtk_box_pack_end(gui->box, gui->input, false, false, 0);
641 /* initialize the modes */
643 mode_add('n', normal_enter, normal_leave, normal_keypress, NULL);
644 mode_add('c', ex_enter, ex_leave, ex_keypress, ex_input_changed);
645 mode_add('i', input_enter, input_leave, input_keypress, NULL);
646 mode_add('p', pass_enter, pass_leave, pass_keypress, NULL);
654 /* initially apply input style */
655 vb_update_input_style();
659 /* enter normal mode */
662 /* make sure the main window and all its contents are visible */
663 gtk_widget_show_all(gui->window);
666 static void read_config(void)
670 /* load default config */
671 for (guint i = 0; default_config[i] != NULL; i++) {
672 if (!ex_run_string(default_config[i])) {
673 fprintf(stderr, "Invalid default config: %s\n", default_config[i]);
677 /* read config from config files */
678 lines = util_get_lines(vb.files[FILES_CONFIG]);
681 int length = g_strv_length(lines) - 1;
682 for (int i = 0; i < length; i++) {
687 if (!ex_run_string(line)) {
688 fprintf(stderr, "Invalid config: %s\n", line);
695 static void setup_signals()
697 /* Set up callbacks so that if either the main window or the browser
698 * instance is closed, the program will exit */
699 g_signal_connect(vb.gui.window, "destroy", G_CALLBACK(destroy_window_cb), NULL);
701 G_OBJECT(vb.gui.webview),
702 "signal::notify::progress", G_CALLBACK(webview_progress_cb), NULL,
703 "signal::notify::load-status", G_CALLBACK(webview_load_status_cb), NULL,
704 "signal::button-release-event", G_CALLBACK(button_relase_cb), NULL,
705 "signal::new-window-policy-decision-requested", G_CALLBACK(new_window_policy_cb), NULL,
706 "signal::create-web-view", G_CALLBACK(create_web_view_cb), NULL,
707 "signal::hovering-over-link", G_CALLBACK(hover_link_cb), NULL,
708 "signal::title-changed", G_CALLBACK(title_changed_cb), NULL,
709 "signal::mime-type-policy-decision-requested", G_CALLBACK(mimetype_decision_cb), NULL,
710 "signal::download-requested", G_CALLBACK(vb_download), NULL,
711 "signal::should-show-delete-interface-for-element", G_CALLBACK(gtk_false), NULL,
715 #ifdef FEATURE_NO_SCROLLBARS
716 WebKitWebFrame *frame = webkit_web_view_get_main_frame(vb.gui.webview);
717 g_signal_connect(G_OBJECT(frame), "scrollbars-policy-changed", G_CALLBACK(gtk_true), NULL);
721 G_OBJECT(vb.gui.window), "key-press-event", G_CALLBACK(map_keypress), NULL
724 G_OBJECT(vb.gui.input), "focus-in-event", G_CALLBACK(mode_input_focusin), NULL
727 G_OBJECT(vb.gui.buffer),
728 "signal::changed", G_CALLBACK(mode_input_changed), NULL,
732 /* webview adjustment */
733 g_object_connect(G_OBJECT(vb.gui.adjust_v),
734 "signal::value-changed", G_CALLBACK(scroll_cb), NULL,
740 G_OBJECT(vb.gui.inspector),
741 "signal::inspect-web-view", G_CALLBACK(inspector_new), NULL,
742 "signal::show-window", G_CALLBACK(inspector_show), NULL,
743 "signal::close-window", G_CALLBACK(inspector_close), NULL,
744 "signal::finished", G_CALLBACK(inspector_finished), NULL,
749 static void init_files(void)
751 char *path = util_get_config_dir();
753 if (vb.custom_config) {
754 char *rp = realpath(vb.custom_config, NULL);
755 vb.files[FILES_CONFIG] = g_strdup(rp);
758 vb.files[FILES_CONFIG] = g_build_filename(path, "config", NULL);
759 util_create_file_if_not_exists(vb.files[FILES_CONFIG]);
762 #ifdef FEATURE_COOKIE
763 vb.files[FILES_COOKIE] = g_build_filename(path, "cookies", NULL);
764 util_create_file_if_not_exists(vb.files[FILES_COOKIE]);
767 vb.files[FILES_CLOSED] = g_build_filename(path, "closed", NULL);
768 util_create_file_if_not_exists(vb.files[FILES_CLOSED]);
770 vb.files[FILES_HISTORY] = g_build_filename(path, "history", NULL);
771 util_create_file_if_not_exists(vb.files[FILES_HISTORY]);
773 vb.files[FILES_COMMAND] = g_build_filename(path, "command", NULL);
774 util_create_file_if_not_exists(vb.files[FILES_COMMAND]);
776 vb.files[FILES_SEARCH] = g_build_filename(path, "search", NULL);
777 util_create_file_if_not_exists(vb.files[FILES_SEARCH]);
779 vb.files[FILES_BOOKMARK] = g_build_filename(path, "bookmark", NULL);
780 util_create_file_if_not_exists(vb.files[FILES_BOOKMARK]);
783 vb.files[FILES_QUEUE] = g_build_filename(path, "queue", NULL);
784 util_create_file_if_not_exists(vb.files[FILES_QUEUE]);
787 vb.files[FILES_SCRIPT] = g_build_filename(path, "scripts.js", NULL);
789 vb.files[FILES_USER_STYLE] = g_build_filename(path, "style.css", NULL);
794 static gboolean button_relase_cb(WebKitWebView *webview, GdkEventButton *event)
796 gboolean propagate = false;
797 WebKitHitTestResultContext context;
799 WebKitHitTestResult *result = webkit_web_view_get_hit_test_result(webview, event);
801 g_object_get(result, "context", &context, NULL);
802 if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE) {
805 } else if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK && event->button == 2) {
806 /* middle mouse click onto link */
807 Arg a = {VB_TARGET_NEW};
808 g_object_get(result, "link-uri", &a.s, NULL);
813 g_object_unref(result);
818 static gboolean new_window_policy_cb(
819 WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *request,
820 WebKitWebNavigationAction *navig, WebKitWebPolicyDecision *policy)
822 if (webkit_web_navigation_action_get_reason(navig) == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
823 webkit_web_policy_decision_ignore(policy);
824 /* open in a new window */
825 Arg a = {VB_TARGET_NEW, (char*)webkit_network_request_get_uri(request)};
832 static WebKitWebView *create_web_view_cb(WebKitWebView *view, WebKitWebFrame *frame)
834 WebKitWebView *new = WEBKIT_WEB_VIEW(webkit_web_view_new());
836 /* wait until the new webview receives its new URI */
837 g_signal_connect(new, "notify::uri", G_CALLBACK(create_web_view_received_uri_cb), NULL);
842 static void create_web_view_received_uri_cb(WebKitWebView *view)
844 Arg a = {VB_TARGET_NEW, (char*)webkit_web_view_get_uri(view)};
845 /* destroy temporary webview */
846 webkit_web_view_stop_loading(view);
847 gtk_widget_destroy(GTK_WIDGET(view));
851 static void hover_link_cb(WebKitWebView *webview, const char *title, const char *link)
855 message = g_strconcat("Link: ", link, NULL);
856 gtk_label_set_text(GTK_LABEL(vb.gui.statusbar.left), message);
859 vb_update_urlbar(webkit_web_view_get_uri(webview));
863 static void title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, const char *title)
865 OVERWRITE_STRING(vb.state.title, title);
869 static void update_title(void)
871 #ifdef FEATURE_TITLE_PROGRESS
872 /* show load status of page or the downloads */
873 if (vb.state.progress != 100) {
874 char *title = g_strdup_printf(
877 vb.state.title ? vb.state.title : ""
879 gtk_window_set_title(GTK_WINDOW(vb.gui.window), title);
884 if (vb.state.title) {
885 gtk_window_set_title(GTK_WINDOW(vb.gui.window), vb.state.title);
889 static gboolean mimetype_decision_cb(WebKitWebView *webview,
890 WebKitWebFrame *frame, WebKitNetworkRequest *request, char *mime_type,
891 WebKitWebPolicyDecision *decision)
893 /* don't start download if request failed or stopped by proxy */
894 if (!mime_type || *mime_type == '\0') {
897 if (webkit_web_view_can_show_mime_type(webview, mime_type) == false) {
898 webkit_web_policy_decision_download(decision);
905 gboolean vb_download(WebKitWebView *view, WebKitDownload *download, const char *path)
907 WebKitDownloadStatus status;
910 /* prepare the path to save the donwload */
912 file = util_build_path(path, vb.config.download_dir);
914 path = webkit_download_get_suggested_filename(download);
916 path = PROJECT "-donwload";
918 file = util_build_path(path, vb.config.download_dir);
921 /* build the file uri from file path */
922 uri = g_filename_to_uri(file, NULL, NULL);
923 webkit_download_set_destination_uri(download, uri);
927 guint64 size = webkit_download_get_total_size(download);
929 vb_echo(VB_MSG_NORMAL, false, "Download %s [~%uB] started ...", path, size);
931 vb_echo(VB_MSG_NORMAL, false, "Download %s started ...", path);
934 status = webkit_download_get_status(download);
935 if (status == WEBKIT_DOWNLOAD_STATUS_CREATED) {
936 webkit_download_start(download);
939 /* prepend the download to the download list */
940 vb.state.downloads = g_list_prepend(vb.state.downloads, download);
942 /* connect signal handler to check if the download is done */
943 g_signal_connect(download, "notify::status", G_CALLBACK(download_progress_cp), NULL);
944 g_signal_connect(download, "notify::progress", G_CALLBACK(webview_download_progress_cb), NULL);
946 vb_update_statusbar();
951 static void download_progress_cp(WebKitDownload *download, GParamSpec *pspec)
953 WebKitDownloadStatus status = webkit_download_get_status(download);
955 if (status == WEBKIT_DOWNLOAD_STATUS_STARTED || status == WEBKIT_DOWNLOAD_STATUS_CREATED) {
959 const char *file = webkit_download_get_destination_uri(download);
960 /* skip the file protocoll for the display */
961 if (!strncmp(file, "file://", 7)) {
964 if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED) {
965 vb_echo(VB_MSG_ERROR, false, "Error downloading %s", file);
967 vb_echo(VB_MSG_NORMAL, false, "Download %s finished", file);
970 /* remove the donwload from the list */
971 vb.state.downloads = g_list_remove(vb.state.downloads, download);
973 vb_update_statusbar();
976 int main(int argc, char *argv[])
978 static char *winid = NULL;
979 static gboolean ver = false;
982 vb.custom_config = NULL;
983 static GOptionEntry opts[] = {
984 {"version", 'v', 0, G_OPTION_ARG_NONE, &ver, "Print version", NULL},
985 {"config", 'c', 0, G_OPTION_ARG_STRING, &vb.custom_config, "Custom cufiguration file", NULL},
986 {"embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "Reparents to window specified by xid", NULL},
989 /* Initialize GTK+ */
990 if (!gtk_init_with_args(&argc, &argv, "[URI]", opts, NULL, &err)) {
991 g_printerr("can't init gtk: %s\n", err->message);
998 fprintf(stdout, "%s/%s\n", PROJECT, VERSION);
1002 /* save arguments */
1006 vb.embed = strtol(winid, NULL, 0);
1011 /* command line argument: URL */
1012 vb_load_uri(&(Arg){VB_TARGET_CURRENT, argc > 1 ? argv[argc - 1] : NULL});
1014 /* Run the main GTK+ event loop */
1017 return EXIT_SUCCESS;