2 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
4 * Copyright (c) 2010, 2011, 2012 Edd Barrett <vext01@gmail.com>
5 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
6 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
7 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 char *version
= XXXTERM_VERSION
;
28 uint32_t swm_debug
= 0
51 GCRY_THREAD_OPTION_PTHREAD_IMPL
;
63 TAILQ_ENTRY(session
) entry
;
66 TAILQ_HEAD(session_list
, session
);
69 TAILQ_ENTRY(undo
) entry
;
72 int back
; /* Keeps track of how many back
73 * history items there are. */
75 TAILQ_HEAD(undo_tailq
, undo
);
77 struct command_entry
{
79 TAILQ_ENTRY(command_entry
) entry
;
81 TAILQ_HEAD(command_list
, command_entry
);
84 #define XT_DIR (".xxxterm")
85 #define XT_CACHE_DIR ("cache")
86 #define XT_CERT_DIR ("certs")
87 #define XT_JS_DIR ("js")
88 #define XT_SESSIONS_DIR ("sessions")
89 #define XT_TEMP_DIR ("tmp")
90 #define XT_CONF_FILE ("xxxterm.conf")
91 #define XT_QMARKS_FILE ("quickmarks")
92 #define XT_SAVED_TABS_FILE ("main_session")
93 #define XT_RESTART_TABS_FILE ("restart_tabs")
94 #define XT_SOCKET_FILE ("socket")
95 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
96 #define XT_SEARCH_FILE ("search_history")
97 #define XT_COMMAND_FILE ("command_history")
98 #define XT_DLMAN_REFRESH "10"
99 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
100 #define XT_MAX_UNDO_CLOSE_TAB (32)
101 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
102 #define XT_PRINT_EXTRA_MARGIN 10
103 #define XT_URL_REGEX ("^[[:blank:]]*[^[:blank:]]*([[:alnum:]-]+\\.)+[[:alnum:]-][^[:blank:]]*[[:blank:]]*$")
104 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
107 #define XT_COLOR_RED "#cc0000"
108 #define XT_COLOR_YELLOW "#ffff66"
109 #define XT_COLOR_BLUE "lightblue"
110 #define XT_COLOR_GREEN "#99ff66"
111 #define XT_COLOR_WHITE "white"
112 #define XT_COLOR_BLACK "black"
114 #define XT_COLOR_CT_BACKGROUND "#000000"
115 #define XT_COLOR_CT_INACTIVE "#dddddd"
116 #define XT_COLOR_CT_ACTIVE "#bbbb00"
117 #define XT_COLOR_CT_SEPARATOR "#555555"
119 #define XT_COLOR_SB_SEPARATOR "#555555"
121 #define XT_PROTO_DELIM "://"
124 #define XT_MOVE_INVALID (0)
125 #define XT_MOVE_DOWN (1)
126 #define XT_MOVE_UP (2)
127 #define XT_MOVE_BOTTOM (3)
128 #define XT_MOVE_TOP (4)
129 #define XT_MOVE_PAGEDOWN (5)
130 #define XT_MOVE_PAGEUP (6)
131 #define XT_MOVE_HALFDOWN (7)
132 #define XT_MOVE_HALFUP (8)
133 #define XT_MOVE_LEFT (9)
134 #define XT_MOVE_FARLEFT (10)
135 #define XT_MOVE_RIGHT (11)
136 #define XT_MOVE_FARRIGHT (12)
137 #define XT_MOVE_PERCENT (13)
138 #define XT_MOVE_CENTER (14)
140 #define XT_QMARK_SET (0)
141 #define XT_QMARK_OPEN (1)
142 #define XT_QMARK_TAB (2)
144 #define XT_MARK_SET (0)
145 #define XT_MARK_GOTO (1)
147 #define XT_GO_UP_ROOT (999)
149 #define XT_TAB_LAST (-4)
150 #define XT_TAB_FIRST (-3)
151 #define XT_TAB_PREV (-2)
152 #define XT_TAB_NEXT (-1)
153 #define XT_TAB_INVALID (0)
154 #define XT_TAB_NEW (1)
155 #define XT_TAB_DELETE (2)
156 #define XT_TAB_DELQUIT (3)
157 #define XT_TAB_OPEN (4)
158 #define XT_TAB_UNDO_CLOSE (5)
159 #define XT_TAB_SHOW (6)
160 #define XT_TAB_HIDE (7)
161 #define XT_TAB_NEXTSTYLE (8)
162 #define XT_TAB_LOAD_IMAGES (9)
164 #define XT_NAV_INVALID (0)
165 #define XT_NAV_BACK (1)
166 #define XT_NAV_FORWARD (2)
167 #define XT_NAV_RELOAD (3)
168 #define XT_NAV_STOP (4)
170 #define XT_FOCUS_INVALID (0)
171 #define XT_FOCUS_URI (1)
172 #define XT_FOCUS_SEARCH (2)
174 #define XT_SEARCH_INVALID (0)
175 #define XT_SEARCH_NEXT (1)
176 #define XT_SEARCH_PREV (2)
178 #define XT_STYLE_CURRENT_TAB (0)
179 #define XT_STYLE_GLOBAL (1)
181 #define XT_PASTE_CURRENT_TAB (0)
182 #define XT_PASTE_NEW_TAB (1)
184 #define XT_ZOOM_IN (-1)
185 #define XT_ZOOM_OUT (-2)
186 #define XT_ZOOM_NORMAL (100)
188 #define XT_URL_SHOW (1)
189 #define XT_URL_HIDE (2)
191 #define XT_CMD_OPEN (0)
192 #define XT_CMD_OPEN_CURRENT (1)
193 #define XT_CMD_TABNEW (2)
194 #define XT_CMD_TABNEW_CURRENT (3)
196 #define XT_STATUS_NOTHING (0)
197 #define XT_STATUS_LINK (1)
198 #define XT_STATUS_URI (2)
199 #define XT_STATUS_LOADING (3)
201 #define XT_SES_DONOTHING (0)
202 #define XT_SES_CLOSETABS (1)
204 #define XT_PREFIX (1<<0)
205 #define XT_USERARG (1<<1)
206 #define XT_URLARG (1<<2)
207 #define XT_INTARG (1<<3)
208 #define XT_SESSARG (1<<4)
209 #define XT_SETARG (1<<5)
211 #define XT_HINT_NEWTAB (1<<0)
213 #define XT_BUFCMD_SZ (8)
215 #define XT_EJS_SHOW (1<<0)
217 GtkWidget
* create_button(char *, char *, int);
219 void recalc_tabs(void);
220 void recolor_compact_tabs(void);
221 void set_current_tab(int page_num
);
222 gboolean
update_statusbar_position(GtkAdjustment
*, gpointer
);
223 void marks_clear(struct tab
*t
);
226 extern char *__progname
;
227 char * const *start_argv
;
229 GtkWidget
*main_window
;
230 GtkNotebook
*notebook
;
232 GtkWidget
*arrow
, *abtn
;
233 struct tab_list tabs
;
234 struct history_list hl
;
235 int hl_purge_count
= 0;
236 struct session_list sessions
;
237 struct domain_list c_wl
;
238 struct domain_list js_wl
;
239 struct domain_list pl_wl
;
240 struct strict_transport_tree st_tree
;
241 struct undo_tailq undos
;
242 struct keybinding_list kbl
;
244 struct user_agent_list ua_list
;
245 int user_agent_count
= 0;
246 struct command_list chl
;
247 struct command_list shl
;
248 struct command_entry
*history_at
;
249 struct command_entry
*search_at
;
251 int cmd_history_count
= 0;
252 int search_history_count
= 0;
254 uint64_t blocked_cookies
= 0;
255 char named_session
[PATH_MAX
];
256 GtkListStore
*completion_model
;
257 GtkListStore
*buffers_store
;
259 char *qmarks
[XT_NOQMARKS
];
260 int btn_down
; /* M1 down in any wv */
261 regex_t url_re
; /* guess_search regex */
263 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
264 int next_download_id
= 1;
266 void xxx_dir(char *);
267 int icon_size_map(int);
268 void activate_uri_entry_cb(GtkWidget
*, struct tab
*);
271 history_delete(struct command_list
*l
, int *counter
)
273 struct command_entry
*c
;
275 if (l
== NULL
|| counter
== NULL
)
278 c
= TAILQ_LAST(l
, command_list
);
282 TAILQ_REMOVE(l
, c
, entry
);
289 history_add(struct command_list
*list
, char *file
, char *l
, int *counter
)
291 struct command_entry
*c
;
294 if (list
== NULL
|| l
== NULL
|| counter
== NULL
)
297 /* don't add the same line */
298 c
= TAILQ_FIRST(list
);
300 if (!strcmp(c
->line
+ 1 /* skip space */, l
))
303 c
= g_malloc0(sizeof *c
);
304 c
->line
= g_strdup_printf(" %s", l
);
307 TAILQ_INSERT_HEAD(list
, c
, entry
);
310 history_delete(list
, counter
);
312 if (history_autosave
&& file
) {
313 f
= fopen(file
, "w");
315 show_oops(NULL
, "couldn't write history %s", file
);
319 TAILQ_FOREACH_REVERSE(c
, list
, command_list
, entry
) {
321 fprintf(f
, "%s\n", c
->line
);
329 history_read(struct command_list
*list
, char *file
, int *counter
)
332 char *s
, line
[65536];
334 if (list
== NULL
|| file
== NULL
)
337 f
= fopen(file
, "r");
339 startpage_add("couldn't open history file %s", file
);
344 s
= fgets(line
, sizeof line
, f
);
345 if (s
== NULL
|| feof(f
) || ferror(f
))
347 if ((s
= strchr(line
, '\n')) == NULL
) {
348 startpage_add("invalid history file %s", file
);
354 history_add(list
, NULL
, line
+ 1, counter
);
362 /* marks array storage. */
366 if (i
< 0 || i
>= XT_NOMARKS
)
377 if ((ret
= strchr(XT_MARKS
, m
)) != NULL
)
378 return ret
- XT_MARKS
;
383 /* quickmarks array storage. */
387 if (i
< 0 || i
>= XT_NOQMARKS
)
398 if ((ret
= strchr(XT_QMARKS
, m
)) != NULL
)
399 return ret
- XT_QMARKS
;
404 #ifndef XT_SIGNALS_DISABLE
408 int saved_errno
, status
;
413 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
417 if (errno
!= ECHILD
) {
419 clog_warn("sigchild: waitpid:");
425 if (WIFEXITED(status
)) {
426 if (WEXITSTATUS(status
) != 0) {
428 clog_warnx("sigchild: child exit status: %d",
429 WEXITSTATUS(status));
434 clog_warnx("sigchild: child is terminated abnormally");
444 is_g_object_setting(GObject
*o
, char *str
)
446 guint n_props
= 0, i
;
447 GParamSpec
**proplist
;
453 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
456 for (i
= 0; i
< n_props
; i
++) {
457 if (! strcmp(proplist
[i
]->name
, str
)) {
468 get_current_tab(void)
472 TAILQ_FOREACH(t
, &tabs
, entry
) {
473 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
477 warnx("%s: no current tab", __func__
);
483 set_status(struct tab
*t
, gchar
*s
, int status
)
491 case XT_STATUS_LOADING
:
492 type
= g_strdup_printf("Loading: %s", s
);
496 type
= g_strdup_printf("Link: %s", s
);
498 t
->status
= g_strdup(gtk_entry_get_text(
499 GTK_ENTRY(t
->sbe
.statusbar
)));
503 type
= g_strdup_printf("%s", s
);
505 t
->status
= g_strdup(type
);
509 t
->status
= g_strdup(s
);
511 case XT_STATUS_NOTHING
:
516 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
522 hide_cmd(struct tab
*t
)
524 DNPRINTF(XT_D_CMD
, "%s: tab %d\n", __func__
, t
->tab_id
);
526 history_at
= NULL
; /* just in case */
527 search_at
= NULL
; /* just in case */
528 gtk_widget_hide(t
->cmd
);
532 show_cmd(struct tab
*t
)
534 DNPRINTF(XT_D_CMD
, "%s: tab %d\n", __func__
, t
->tab_id
);
538 gtk_widget_hide(t
->oops
);
539 gtk_widget_show(t
->cmd
);
543 hide_buffers(struct tab
*t
)
545 gtk_widget_hide(t
->buffers
);
546 gtk_list_store_clear(buffers_store
);
557 sort_tabs_by_page_num(struct tab
***stabs
)
562 num_tabs
= gtk_notebook_get_n_pages(notebook
);
564 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
566 TAILQ_FOREACH(t
, &tabs
, entry
)
567 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
573 buffers_make_list(void)
576 const gchar
*title
= NULL
;
578 struct tab
**stabs
= NULL
;
580 num_tabs
= sort_tabs_by_page_num(&stabs
);
582 for (i
= 0; i
< num_tabs
; i
++)
584 gtk_list_store_append(buffers_store
, &iter
);
585 title
= get_title(stabs
[i
], FALSE
);
586 gtk_list_store_set(buffers_store
, &iter
,
587 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
589 COL_FAVICON
, gtk_image_get_pixbuf
590 (GTK_IMAGE(stabs
[i
]->tab_elems
.favicon
)),
599 show_buffers(struct tab
*t
)
601 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)))
605 gtk_widget_show(t
->buffers
);
606 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
610 toggle_buffers(struct tab
*t
)
612 if (gtk_widget_get_visible(t
->buffers
))
619 buffers(struct tab
*t
, struct karg
*args
)
627 hide_oops(struct tab
*t
)
629 gtk_widget_hide(t
->oops
);
633 show_oops(struct tab
*at
, const char *fmt
, ...)
637 struct tab
*t
= NULL
;
643 if ((t
= get_current_tab()) == NULL
)
649 if (vasprintf(&msg
, fmt
, ap
) == -1)
650 errx(1, "show_oops failed");
653 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
654 gtk_widget_hide(t
->cmd
);
655 gtk_widget_show(t
->oops
);
661 char work_dir
[PATH_MAX
];
662 char certs_dir
[PATH_MAX
];
663 char js_dir
[PATH_MAX
];
664 char cache_dir
[PATH_MAX
];
665 char sessions_dir
[PATH_MAX
];
666 char temp_dir
[PATH_MAX
];
667 char cookie_file
[PATH_MAX
];
668 char *strict_transport_file
= NULL
;
669 SoupSession
*session
;
670 SoupCookieJar
*s_cookiejar
;
671 SoupCookieJar
*p_cookiejar
;
672 char rc_fname
[PATH_MAX
];
674 struct mime_type_list mtl
;
675 struct alias_list aliases
;
678 struct tab
*create_new_tab(char *, struct undo
*, int, int);
679 void delete_tab(struct tab
*);
680 void setzoom_webkit(struct tab
*, int);
681 int download_rb_cmp(struct download
*, struct download
*);
682 gboolean
cmd_execute(struct tab
*t
, char *str
);
685 history_rb_cmp(struct history
*h1
, struct history
*h2
)
687 return (strcmp(h1
->uri
, h2
->uri
));
689 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
692 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
694 return (strcmp(d1
->d
, d2
->d
));
696 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
699 download_rb_cmp(struct download
*e1
, struct download
*e2
)
701 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
703 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
705 struct valid_url_types
{
716 valid_url_type(char *url
)
720 for (i
= 0; i
< LENGTH(vut
); i
++)
721 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
728 match_alias(char *url_in
)
732 char *url_out
= NULL
, *search
, *enc_arg
;
734 search
= g_strdup(url_in
);
736 if (strsep(&arg
, " \t") == NULL
) {
737 show_oops(NULL
, "match_alias: NULL URL");
741 TAILQ_FOREACH(a
, &aliases
, entry
) {
742 if (!strcmp(search
, a
->a_name
))
747 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
750 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
751 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
754 url_out
= g_strdup_printf(a
->a_uri
, "");
762 guess_url_type(char *url_in
)
765 char *url_out
= NULL
, *enc_search
= NULL
;
770 /* substitute aliases */
771 url_out
= match_alias(url_in
);
775 /* see if we are an about page */
776 if (!strncmp(url_in
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
777 for (i
= 0; i
< about_list_size(); i
++)
778 if (!strcmp(&url_in
[XT_URI_ABOUT_LEN
],
779 about_list
[i
].name
)) {
780 url_out
= g_strdup(url_in
);
784 if (guess_search
&& url_regex
&&
785 !(g_str_has_prefix(url_in
, "http://") ||
786 g_str_has_prefix(url_in
, "https://"))) {
787 if (regexec(&url_re
, url_in
, 0, NULL
, 0)) {
788 /* invalid URI so search instead */
789 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
790 url_out
= g_strdup_printf(search_string
, enc_search
);
796 /* XXX not sure about this heuristic */
797 if (stat(url_in
, &sb
) == 0) {
798 if (url_in
[0] == '/')
799 url_out
= g_strdup_printf("file://%s", url_in
);
801 cwd
= malloc(PATH_MAX
);
802 if (getcwd(cwd
, PATH_MAX
) != NULL
) {
803 url_out
= g_strdup_printf("file://%s/%s",cwd
, url_in
);
808 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
810 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
816 load_uri(struct tab
*t
, gchar
*uri
)
819 gchar
*newuri
= NULL
;
825 /* Strip leading spaces. */
826 while (*uri
&& isspace(*uri
))
829 if (strlen(uri
) == 0) {
834 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
836 if (valid_url_type(uri
)) {
837 newuri
= guess_url_type(uri
);
841 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
842 for (i
= 0; i
< about_list_size(); i
++)
843 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
844 bzero(&args
, sizeof args
);
845 about_list
[i
].func(t
, &args
);
846 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
850 show_oops(t
, "invalid about page");
854 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
856 webkit_web_view_load_uri(t
->wv
, uri
);
863 get_uri(struct tab
*t
)
865 const gchar
*uri
= NULL
;
867 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
869 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
870 uri
= webkit_web_view_get_uri(t
->wv
);
872 /* use tmp_uri to make sure it is g_freed */
875 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
876 about_list
[t
->xtp_meaning
].name
);
883 get_title(struct tab
*t
, bool window
)
885 const gchar
*set
= NULL
, *title
= NULL
;
886 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
888 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
889 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
892 title
= webkit_web_view_get_title(t
->wv
);
893 if ((set
= title
? title
: get_uri(t
)))
897 set
= window
? XT_NAME
: "(untitled)";
903 find_mime_type(char *mime_type
)
905 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
907 TAILQ_FOREACH(m
, &mtl
, entry
) {
909 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
912 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
925 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
931 if (s
== NULL
|| wl
== NULL
)
934 if (!strncmp(s
, "http://", strlen("http://")))
935 s
= &s
[strlen("http://")];
936 else if (!strncmp(s
, "https://", strlen("https://")))
937 s
= &s
[strlen("https://")];
942 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
943 /* chop string at first slash */
944 if (s
[i
] == '/' || s
[i
] == ':' || s
[i
] == '\0') {
956 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
962 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
966 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
969 JSStringGetUTF8CString(jsref
, s
, l
);
970 JSStringRelease(jsref
);
975 #define XT_JS_DONE ("done;")
976 #define XT_JS_DONE_LEN (strlen(XT_JS_DONE))
977 #define XT_JS_INSERT ("insert;")
978 #define XT_JS_INSERT_LEN (strlen(XT_JS_INSERT))
981 run_script(struct tab
*t
, char *s
)
983 JSGlobalContextRef ctx
;
984 WebKitWebFrame
*frame
;
986 JSValueRef val
, exception
;
989 DNPRINTF(XT_D_JS
, "%s: tab %d %s\n", __func__
,
990 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
992 frame
= webkit_web_view_get_main_frame(t
->wv
);
993 ctx
= webkit_web_frame_get_global_context(frame
);
995 str
= JSStringCreateWithUTF8CString(s
);
996 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
997 NULL
, 0, &exception
);
998 JSStringRelease(str
);
1000 DNPRINTF(XT_D_JS
, "%s: val %p\n", __func__
, val
);
1002 es
= js_ref_to_string(ctx
, exception
);
1004 DNPRINTF(XT_D_JS
, "%s: exception %s\n", __func__
, es
);
1009 es
= js_ref_to_string(ctx
, val
);
1012 if (!strncmp(es
, XT_JS_DONE
, XT_JS_DONE_LEN
))
1014 if (!strncmp(es
, XT_JS_INSERT
, XT_JS_INSERT_LEN
))
1018 DNPRINTF(XT_D_JS
, "%s: val %s\n", __func__
, es
);
1027 enable_hints(struct tab
*t
)
1029 DNPRINTF(XT_D_JS
, "%s: tab %d\n", __func__
, t
->tab_id
);
1032 run_script(t
, "hints.createHints('', 'F');");
1034 run_script(t
, "hints.createHints('', 'f');");
1035 t
->mode
= XT_MODE_HINT
;
1039 disable_hints(struct tab
*t
)
1041 DNPRINTF(XT_D_JS
, "%s: tab %d\n", __func__
, t
->tab_id
);
1043 run_script(t
, "hints.clearHints();");
1044 t
->mode
= XT_MODE_COMMAND
;
1049 passthrough(struct tab
*t
, struct karg
*args
)
1051 t
->mode
= XT_MODE_PASSTHROUGH
;
1056 modurl(struct tab
*t
, struct karg
*args
)
1058 const gchar
*uri
= NULL
;
1061 /* XXX kind of a bad hack, but oh well */
1062 if (GTK_WIDGET_HAS_FOCUS(t
->uri_entry
)) {
1063 if ((uri
= gtk_entry_get_text(GTK_ENTRY(t
->uri_entry
))) &&
1065 u
= g_strdup_printf("www.%s.com", uri
);
1066 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), u
);
1068 activate_uri_entry_cb(t
->uri_entry
, t
);
1075 hint(struct tab
*t
, struct karg
*args
)
1078 DNPRINTF(XT_D_JS
, "hint: tab %d args %d\n", t
->tab_id
, args
->i
);
1080 if (t
->mode
== XT_MODE_HINT
) {
1081 if (args
->i
== XT_HINT_NEWTAB
)
1091 apply_style(struct tab
*t
)
1094 g_object_set(G_OBJECT(t
->settings
),
1095 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
1099 remove_style(struct tab
*t
)
1102 g_object_set(G_OBJECT(t
->settings
),
1103 "user-stylesheet-uri", NULL
, (char *)NULL
);
1107 userstyle(struct tab
*t
, struct karg
*args
)
1111 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
1114 case XT_STYLE_CURRENT_TAB
:
1120 case XT_STYLE_GLOBAL
:
1121 if (userstyle_global
) {
1122 userstyle_global
= 0;
1123 TAILQ_FOREACH(tt
, &tabs
, entry
)
1126 userstyle_global
= 1;
1127 TAILQ_FOREACH(tt
, &tabs
, entry
)
1137 quit(struct tab
*t
, struct karg
*args
)
1139 if (save_global_history
)
1140 save_global_history_to_disk(t
);
1148 restore_sessions_list(void)
1151 struct dirent
*dp
= NULL
;
1155 sdir
= opendir(sessions_dir
);
1157 while ((dp
= readdir(sdir
)) != NULL
) {
1158 #if defined __MINGW32__
1159 reg
= 1; /* windows only has regular files */
1161 reg
= dp
->d_type
== DT_REG
;
1164 s
= g_malloc(sizeof(struct session
));
1165 s
->name
= g_strdup(dp
->d_name
);
1166 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
1174 open_tabs(struct tab
*t
, struct karg
*a
)
1176 char file
[PATH_MAX
];
1180 struct tab
*ti
, *tt
;
1185 ti
= TAILQ_LAST(&tabs
, tab_list
);
1187 snprintf(file
, sizeof file
, "%s" PS
"%s", sessions_dir
, a
->s
);
1188 if ((f
= fopen(file
, "r")) == NULL
)
1192 if ((uri
= fparseln(f
, NULL
, NULL
, "\0\0\0", 0)) == NULL
)
1193 if (feof(f
) || ferror(f
))
1196 /* retrieve session name */
1197 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
1198 strlcpy(named_session
,
1199 &uri
[strlen(XT_SAVE_SESSION_ID
)],
1200 sizeof named_session
);
1204 if (uri
&& strlen(uri
))
1205 create_new_tab(uri
, NULL
, 1, -1);
1211 /* close open tabs */
1212 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
1214 tt
= TAILQ_FIRST(&tabs
);
1235 restore_saved_tabs(void)
1237 char file
[PATH_MAX
];
1238 int unlink_file
= 0;
1243 snprintf(file
, sizeof file
, "%s" PS
"%s",
1244 sessions_dir
, XT_RESTART_TABS_FILE
);
1245 if (stat(file
, &sb
) == -1)
1246 a
.s
= XT_SAVED_TABS_FILE
;
1249 a
.s
= XT_RESTART_TABS_FILE
;
1252 a
.i
= XT_SES_DONOTHING
;
1253 rv
= open_tabs(NULL
, &a
);
1262 save_tabs(struct tab
*t
, struct karg
*a
)
1264 char file
[PATH_MAX
];
1266 int num_tabs
= 0, i
;
1267 struct tab
**stabs
= NULL
;
1269 /* tab may be null here */
1274 snprintf(file
, sizeof file
, "%s" PS
"%s",
1275 sessions_dir
, named_session
);
1277 snprintf(file
, sizeof file
, "%s" PS
"%s", sessions_dir
, a
->s
);
1279 if ((f
= fopen(file
, "w")) == NULL
) {
1280 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
1284 /* save session name */
1285 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
1287 /* Save tabs, in the order they are arranged in the notebook. */
1288 num_tabs
= sort_tabs_by_page_num(&stabs
);
1290 for (i
= 0; i
< num_tabs
; i
++)
1292 if (get_uri(stabs
[i
]) != NULL
)
1293 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
1294 else if (gtk_entry_get_text(GTK_ENTRY(
1295 stabs
[i
]->uri_entry
)))
1296 fprintf(f
, "%s\n", gtk_entry_get_text(GTK_ENTRY(
1297 stabs
[i
]->uri_entry
)));
1302 /* try and make sure this gets to disk NOW. XXX Backup first? */
1303 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
1304 show_oops(t
, "May not have managed to save session: %s",
1314 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
1326 run_page_script(struct tab
*t
, struct karg
*args
)
1329 char *tmp
, script
[PATH_MAX
];
1331 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
1332 if (tmp
[0] == '\0') {
1333 show_oops(t
, "no script specified");
1337 if ((uri
= get_uri(t
)) == NULL
) {
1338 show_oops(t
, "tab is empty, not running script");
1343 snprintf(script
, sizeof script
, "%s" PS
"%s",
1344 pwd
->pw_dir
, &tmp
[1]);
1346 strlcpy(script
, tmp
, sizeof script
);
1348 return (fork_exec(t
, script
, uri
, "can't launch external script", 1));
1352 yank_uri(struct tab
*t
, struct karg
*args
)
1355 GtkClipboard
*clipboard
;
1357 if ((uri
= get_uri(t
)) == NULL
)
1360 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1361 gtk_clipboard_set_text(clipboard
, uri
, -1);
1367 paste_uri(struct tab
*t
, struct karg
*args
)
1369 GtkClipboard
*clipboard
;
1370 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
1372 gchar
*p
= NULL
, *uri
;
1374 /* try primary clipboard first */
1375 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1376 p
= gtk_clipboard_wait_for_text(clipboard
);
1378 /* if it failed get whatever text is in cut_buffer0 */
1379 if (p
== NULL
&& xterm_workaround
)
1380 if (gdk_property_get(gdk_get_default_root_window(),
1382 gdk_atom_intern("STRING", FALSE
),
1384 1024 * 1024 /* picked out of my butt */,
1392 /* yes sir, we need to NUL the string */
1398 while (*uri
&& isspace(*uri
))
1400 if (strlen(uri
) == 0) {
1401 show_oops(t
, "empty paste buffer");
1404 if (guess_search
== 0 && valid_url_type(uri
)) {
1405 /* we can be clever and paste this in search box */
1406 show_oops(t
, "not a valid URL");
1410 if (args
->i
== XT_PASTE_CURRENT_TAB
)
1412 else if (args
->i
== XT_PASTE_NEW_TAB
)
1413 create_new_tab(uri
, NULL
, 1, -1);
1424 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
1429 g_object_get(G_OBJECT(t
->settings
),
1430 "enable-scripts", &es
, (char *)NULL
);
1435 set
= XT_WL_DISABLE
;
1437 a
.i
= set
| XT_WL_TOPLEVEL
;
1440 a
.i
= set
| XT_WL_TOPLEVEL
;
1443 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
1448 toggle_src(struct tab
*t
, struct karg
*args
)
1455 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
1456 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
1457 webkit_web_view_reload(t
->wv
);
1463 focus_webview(struct tab
*t
)
1468 /* only grab focus if we are visible */
1469 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
1470 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
1474 focus(struct tab
*t
, struct karg
*args
)
1476 if (t
== NULL
|| args
== NULL
)
1482 if (args
->i
== XT_FOCUS_URI
)
1483 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
1484 else if (args
->i
== XT_FOCUS_SEARCH
)
1485 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
1491 connect_socket_from_uri(const gchar
*uri
, const gchar
**error_str
, char *domain
,
1495 struct addrinfo hints
, *res
= NULL
, *ai
;
1496 int rv
= -1, s
= -1, on
, error
;
1498 static gchar myerror
[256]; /* this is not thread safe */
1501 *error_str
= myerror
;
1502 if (uri
&& !g_str_has_prefix(uri
, "https://")) {
1503 *error_str
= "invalid URI";
1507 su
= soup_uri_new(uri
);
1509 *error_str
= "invalid soup URI";
1512 if (!SOUP_URI_VALID_FOR_HTTP(su
)) {
1513 *error_str
= "invalid HTTPS URI";
1517 snprintf(port
, sizeof port
, "%d", su
->port
);
1518 bzero(&hints
, sizeof(struct addrinfo
));
1519 hints
.ai_flags
= AI_CANONNAME
;
1520 hints
.ai_family
= AF_UNSPEC
;
1521 hints
.ai_socktype
= SOCK_STREAM
;
1523 if ((error
= getaddrinfo(su
->host
, port
, &hints
, &res
))) {
1524 snprintf(myerror
, sizeof myerror
, "getaddrinfo failed: %s",
1525 gai_strerror(errno
));
1529 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
1535 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
1537 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
1540 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
1543 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) == 0)
1547 snprintf(myerror
, sizeof myerror
,
1548 "could not obtain certificates from: %s",
1554 strlcpy(domain
, su
->host
, domain_sz
);
1561 if (rv
== -1 && s
!= -1)
1569 custom_gnutls_push(void *s
, const void *buf
, size_t len
)
1571 return send((size_t)s
, buf
, len
, 0);
1575 custom_gnutls_pull(void *s
, void *buf
, size_t len
)
1577 return recv((size_t)s
, buf
, len
, 0);
1582 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
1585 gnutls_deinit(gsession
);
1587 gnutls_certificate_free_credentials(xcred
);
1593 start_tls(const gchar
**error_str
, int s
, gnutls_session_t
*gs
,
1594 gnutls_certificate_credentials_t
*xc
)
1596 gnutls_certificate_credentials_t xcred
;
1597 gnutls_session_t gsession
;
1599 static gchar myerror
[1024]; /* this is not thread safe */
1601 if (gs
== NULL
|| xc
== NULL
)
1608 gnutls_certificate_allocate_credentials(&xcred
);
1609 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
1610 GNUTLS_X509_FMT_PEM
);
1612 gnutls_init(&gsession
, GNUTLS_CLIENT
);
1613 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
1614 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
1615 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
1617 /* sockets on windows don't use file descriptors */
1618 gnutls_transport_set_push_function(gsession
, custom_gnutls_push
);
1619 gnutls_transport_set_pull_function(gsession
, custom_gnutls_pull
);
1621 if ((rv
= gnutls_handshake(gsession
)) < 0) {
1622 snprintf(myerror
, sizeof myerror
,
1623 "gnutls_handshake failed %d fatal %d %s",
1625 gnutls_error_is_fatal(rv
),
1626 #if LIBGNUTLS_VERSION_MAJOR >= 2 && LIBGNUTLS_VERSION_MINOR >= 6
1627 gnutls_strerror_name(rv
));
1629 "GNUTLS version is too old to provide human readable error");
1631 stop_tls(gsession
, xcred
);
1635 gnutls_credentials_type_t cred
;
1636 cred
= gnutls_auth_get_type(gsession
);
1637 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
1638 snprintf(myerror
, sizeof myerror
,
1639 "gnutls_auth_get_type failed %d",
1641 stop_tls(gsession
, xcred
);
1649 *error_str
= myerror
;
1654 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
1658 const gnutls_datum_t
*cl
;
1659 gnutls_x509_crt_t
*all_certs
;
1662 if (certs
== NULL
|| cert_count
== NULL
)
1664 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
1666 cl
= gnutls_certificate_get_peers(gsession
, &len
);
1670 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
1671 for (i
= 0; i
< len
; i
++) {
1672 gnutls_x509_crt_init(&all_certs
[i
]);
1673 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
1674 GNUTLS_X509_FMT_PEM
< 0)) {
1688 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
1692 for (i
= 0; i
< cert_count
; i
++)
1693 gnutls_x509_crt_deinit(certs
[i
]);
1698 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
1700 GdkColor c_text
, c_base
;
1702 gdk_color_parse(text
, &c_text
);
1703 gdk_color_parse(base
, &c_base
);
1705 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
1706 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
1707 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
1708 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
1710 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
1711 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
1712 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
1713 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
1717 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
1718 size_t cert_count
, char *domain
)
1721 char cert_buf
[64 * 1024], file
[PATH_MAX
];
1726 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
1729 snprintf(file
, sizeof file
, "%s" PS
"%s", certs_dir
, domain
);
1730 if ((f
= fopen(file
, "w")) == NULL
) {
1731 show_oops(t
, "Can't create cert file %s %s",
1732 file
, strerror(errno
));
1736 for (i
= 0; i
< cert_count
; i
++) {
1737 cert_buf_sz
= sizeof cert_buf
;
1738 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
1739 cert_buf
, &cert_buf_sz
)) {
1740 show_oops(t
, "gnutls_x509_crt_export failed");
1743 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
1744 show_oops(t
, "Can't write certs: %s", strerror(errno
));
1749 /* not the best spot but oh well */
1750 gdk_color_parse(XT_COLOR_BLUE
, &color
);
1751 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
1752 statusbar_modify_attr(t
, XT_COLOR_BLACK
, XT_COLOR_BLUE
);
1765 load_compare_cert(const gchar
*uri
, const gchar
**error_str
)
1767 char domain
[8182], file
[PATH_MAX
];
1768 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
1770 unsigned int error
= 0;
1772 size_t cert_buf_sz
, cert_count
;
1773 enum cert_trust rv
= CERT_UNTRUSTED
;
1774 static gchar serr
[80]; /* this isn't thread safe */
1775 gnutls_session_t gsession
;
1776 gnutls_x509_crt_t
*certs
;
1777 gnutls_certificate_credentials_t xcred
;
1779 DNPRINTF(XT_D_URL
, "%s: %s\n", __func__
, uri
);
1783 if ((s
= connect_socket_from_uri(uri
, error_str
, domain
,
1784 sizeof domain
)) == -1)
1787 DNPRINTF(XT_D_URL
, "%s: fd %d\n", __func__
, s
);
1790 if (start_tls(error_str
, s
, &gsession
, &xcred
))
1792 DNPRINTF(XT_D_URL
, "%s: got tls\n", __func__
);
1794 /* verify certs in case cert file doesn't exist */
1795 if (gnutls_certificate_verify_peers2(gsession
, &error
) !=
1797 *error_str
= "Invalid certificates";
1802 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
1803 *error_str
= "Can't get connection certificates";
1807 snprintf(file
, sizeof file
, "%s" PS
"%s", certs_dir
, domain
);
1808 if ((f
= fopen(file
, "r")) == NULL
) {
1814 for (i
= 0; i
< cert_count
; i
++) {
1815 cert_buf_sz
= sizeof cert_buf
;
1816 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
1817 cert_buf
, &cert_buf_sz
)) {
1820 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
1821 rv
= CERT_BAD
; /* critical */
1824 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
1825 rv
= CERT_BAD
; /* critical */
1834 free_connection_certs(certs
, cert_count
);
1836 /* we close the socket first for speed */
1840 /* only complain if we didn't save it locally */
1841 if (error
&& rv
!= CERT_LOCAL
) {
1842 strlcpy(serr
, "Certificate exception(s): ", sizeof serr
);
1843 if (error
& GNUTLS_CERT_INVALID
)
1844 strlcat(serr
, "invalid, ", sizeof serr
);
1845 if (error
& GNUTLS_CERT_REVOKED
)
1846 strlcat(serr
, "revoked, ", sizeof serr
);
1847 if (error
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
1848 strlcat(serr
, "signer not found, ", sizeof serr
);
1849 if (error
& GNUTLS_CERT_SIGNER_NOT_CA
)
1850 strlcat(serr
, "not signed by CA, ", sizeof serr
);
1851 if (error
& GNUTLS_CERT_INSECURE_ALGORITHM
)
1852 strlcat(serr
, "insecure algorithm, ", sizeof serr
);
1853 #if LIBGNUTLS_VERSION_MAJOR >= 2 && LIBGNUTLS_VERSION_MINOR >= 6
1854 if (error
& GNUTLS_CERT_NOT_ACTIVATED
)
1855 strlcat(serr
, "not activated, ", sizeof serr
);
1856 if (error
& GNUTLS_CERT_EXPIRED
)
1857 strlcat(serr
, "expired, ", sizeof serr
);
1859 for (i
= strlen(serr
) - 1; i
> 0; i
--)
1860 if (serr
[i
] == ',') {
1867 stop_tls(gsession
, xcred
);
1873 cert_cmd(struct tab
*t
, struct karg
*args
)
1875 const gchar
*uri
, *error_str
= NULL
;
1879 gnutls_session_t gsession
;
1880 gnutls_x509_crt_t
*certs
;
1881 gnutls_certificate_credentials_t xcred
;
1886 if (ssl_ca_file
== NULL
) {
1887 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
1891 if ((uri
= get_uri(t
)) == NULL
) {
1892 show_oops(t
, "Invalid URI");
1896 if ((s
= connect_socket_from_uri(uri
, &error_str
, domain
,
1897 sizeof domain
)) == -1) {
1898 show_oops(t
, "%s", error_str
);
1903 if (start_tls(&error_str
, s
, &gsession
, &xcred
))
1907 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
1908 show_oops(t
, "get_connection_certs failed");
1912 if (args
->i
& XT_SHOW
)
1913 show_certs(t
, certs
, cert_count
, "Certificate Chain");
1914 else if (args
->i
& XT_SAVE
)
1915 save_certs(t
, certs
, cert_count
, domain
);
1917 free_connection_certs(certs
, cert_count
);
1919 /* we close the socket first for speed */
1922 stop_tls(gsession
, xcred
);
1923 if (error_str
&& strlen(error_str
))
1924 show_oops(t
, "%s", error_str
);
1929 remove_cookie(int index
)
1935 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
1937 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
1939 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
1943 print_cookie("remove cookie", c
);
1944 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
1949 soup_cookies_free(cf
);
1955 remove_cookie_domain(int domain_id
)
1957 int domain_count
, rv
= 1;
1962 DNPRINTF(XT_D_COOKIE
, "remove_cookie_domain: %d\n", domain_id
);
1965 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
1967 for (domain_count
= 0; cf
; cf
= cf
->next
) {
1970 if (strcmp(last_domain
, c
->domain
) != 0) {
1972 last_domain
= c
->domain
;
1975 if (domain_count
< domain_id
)
1977 else if (domain_count
> domain_id
)
1980 print_cookie("remove cookie", c
);
1981 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
1985 soup_cookies_free(cf
);
1997 DNPRINTF(XT_D_COOKIE
, "remove_cookie_all\n");
1999 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2001 for (; cf
; cf
= cf
->next
) {
2004 print_cookie("remove cookie", c
);
2005 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
2009 soup_cookies_free(cf
);
2015 toplevel_cmd(struct tab
*t
, struct karg
*args
)
2017 js_toggle_cb(t
->js_toggle
, t
);
2023 can_go_back_for_real(struct tab
*t
)
2026 WebKitWebHistoryItem
*item
;
2032 /* rely on webkit to make sure we can go backward when on an about page */
2034 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
2035 return (webkit_web_view_can_go_back(t
->wv
));
2037 /* the back/forwars list is stupid so help determine if we can go back */
2038 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2040 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
2041 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
2049 can_go_forward_for_real(struct tab
*t
)
2052 WebKitWebHistoryItem
*item
;
2058 /* rely on webkit to make sure we can go forward when on an about page */
2060 if (uri
== NULL
|| g_str_has_prefix(uri
, "about:"))
2061 return (webkit_web_view_can_go_forward(t
->wv
));
2063 /* the back/forwars list is stupid so help selecting a different item */
2064 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2066 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
2067 if (strcmp(webkit_web_history_item_get_uri(item
), uri
))
2075 go_back_for_real(struct tab
*t
)
2078 WebKitWebHistoryItem
*item
;
2086 webkit_web_view_go_back(t
->wv
);
2089 /* the back/forwars list is stupid so help selecting a different item */
2090 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2092 i
--, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
2093 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
2094 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
2101 go_forward_for_real(struct tab
*t
)
2104 WebKitWebHistoryItem
*item
;
2112 webkit_web_view_go_forward(t
->wv
);
2115 /* the back/forwars list is stupid so help selecting a different item */
2116 for (i
= 0, item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2118 i
++, item
= webkit_web_back_forward_list_get_nth_item(t
->bfl
, i
)) {
2119 if (strcmp(webkit_web_history_item_get_uri(item
), uri
)) {
2120 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
2127 navaction(struct tab
*t
, struct karg
*args
)
2129 WebKitWebHistoryItem
*item
;
2130 WebKitWebFrame
*frame
;
2132 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
2133 t
->tab_id
, args
->i
);
2135 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
2137 if (args
->i
== XT_NAV_BACK
)
2138 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2140 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
2142 return (XT_CB_PASSTHROUGH
);
2143 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
2145 return (XT_CB_PASSTHROUGH
);
2151 go_back_for_real(t
);
2153 case XT_NAV_FORWARD
:
2155 go_forward_for_real(t
);
2158 frame
= webkit_web_view_get_main_frame(t
->wv
);
2159 webkit_web_frame_reload(frame
);
2162 frame
= webkit_web_view_get_main_frame(t
->wv
);
2163 webkit_web_frame_stop_loading(frame
);
2166 return (XT_CB_PASSTHROUGH
);
2170 move(struct tab
*t
, struct karg
*args
)
2172 GtkAdjustment
*adjust
;
2173 double pi
, si
, pos
, ps
, upper
, lower
, max
;
2179 case XT_MOVE_BOTTOM
:
2181 case XT_MOVE_PAGEDOWN
:
2182 case XT_MOVE_PAGEUP
:
2183 case XT_MOVE_HALFDOWN
:
2184 case XT_MOVE_HALFUP
:
2185 case XT_MOVE_PERCENT
:
2186 case XT_MOVE_CENTER
:
2187 adjust
= t
->adjust_v
;
2190 adjust
= t
->adjust_h
;
2194 pos
= gtk_adjustment_get_value(adjust
);
2195 ps
= gtk_adjustment_get_page_size(adjust
);
2196 upper
= gtk_adjustment_get_upper(adjust
);
2197 lower
= gtk_adjustment_get_lower(adjust
);
2198 si
= gtk_adjustment_get_step_increment(adjust
);
2199 pi
= gtk_adjustment_get_page_increment(adjust
);
2202 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
2203 "max %f si %f pi %f\n",
2204 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
2205 pos
, ps
, upper
, lower
, max
, si
, pi
);
2211 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2216 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2218 case XT_MOVE_BOTTOM
:
2219 case XT_MOVE_FARRIGHT
:
2220 t
->mark
[marktoindex('\'')] = gtk_adjustment_get_value(t
->adjust_v
);
2221 gtk_adjustment_set_value(adjust
, max
);
2224 case XT_MOVE_FARLEFT
:
2225 t
->mark
[marktoindex('\'')] = gtk_adjustment_get_value(t
->adjust_v
);
2226 gtk_adjustment_set_value(adjust
, lower
);
2228 case XT_MOVE_PAGEDOWN
:
2230 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2232 case XT_MOVE_PAGEUP
:
2234 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2236 case XT_MOVE_HALFDOWN
:
2238 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2240 case XT_MOVE_HALFUP
:
2242 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2244 case XT_MOVE_CENTER
:
2245 t
->mark
[marktoindex('\'')] = gtk_adjustment_get_value(t
->adjust_v
);
2246 args
->s
= g_strdup("50.0");
2248 case XT_MOVE_PERCENT
:
2249 t
->mark
[marktoindex('\'')] = gtk_adjustment_get_value(t
->adjust_v
);
2250 percent
= atoi(args
->s
) / 100.0;
2251 pos
= max
* percent
;
2252 if (pos
< 0.0 || pos
> max
)
2254 gtk_adjustment_set_value(adjust
, pos
);
2257 return (XT_CB_PASSTHROUGH
);
2260 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
2262 return (XT_CB_HANDLED
);
2266 url_set_visibility(void)
2270 TAILQ_FOREACH(t
, &tabs
, entry
)
2271 if (show_url
== 0) {
2272 gtk_widget_hide(t
->toolbar
);
2275 gtk_widget_show(t
->toolbar
);
2279 notebook_tab_set_visibility(void)
2281 if (show_tabs
== 0) {
2282 gtk_widget_hide(tab_bar
);
2283 gtk_notebook_set_show_tabs(notebook
, FALSE
);
2285 if (tab_style
== XT_TABS_NORMAL
) {
2286 gtk_widget_hide(tab_bar
);
2287 gtk_notebook_set_show_tabs(notebook
, TRUE
);
2288 } else if (tab_style
== XT_TABS_COMPACT
) {
2289 gtk_widget_show(tab_bar
);
2290 gtk_notebook_set_show_tabs(notebook
, FALSE
);
2296 statusbar_set_visibility(void)
2300 TAILQ_FOREACH(t
, &tabs
, entry
){
2301 if (show_statusbar
== 0)
2302 gtk_widget_hide(t
->statusbar_box
);
2304 gtk_widget_show(t
->statusbar_box
);
2311 url_set(struct tab
*t
, int enable_url_entry
)
2316 show_url
= enable_url_entry
;
2318 if (enable_url_entry
) {
2319 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
2320 GTK_ENTRY_ICON_PRIMARY
, NULL
);
2321 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
2323 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
2324 GTK_ENTRY_ICON_PRIMARY
);
2326 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
2327 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
2328 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
2329 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
2335 fullscreen(struct tab
*t
, struct karg
*args
)
2337 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
2340 return (XT_CB_PASSTHROUGH
);
2342 if (show_url
== 0) {
2350 url_set_visibility();
2351 notebook_tab_set_visibility();
2353 return (XT_CB_HANDLED
);
2357 statustoggle(struct tab
*t
, struct karg
*args
)
2359 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
2361 if (show_statusbar
== 1) {
2363 statusbar_set_visibility();
2364 } else if (show_statusbar
== 0) {
2366 statusbar_set_visibility();
2368 return (XT_CB_HANDLED
);
2372 urlaction(struct tab
*t
, struct karg
*args
)
2374 int rv
= XT_CB_HANDLED
;
2376 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
2379 return (XT_CB_PASSTHROUGH
);
2383 if (show_url
== 0) {
2385 url_set_visibility();
2389 if (show_url
== 1) {
2391 url_set_visibility();
2399 tabaction(struct tab
*t
, struct karg
*args
)
2401 int rv
= XT_CB_HANDLED
;
2402 char *url
= args
->s
;
2406 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
2409 return (XT_CB_PASSTHROUGH
);
2413 if (strlen(url
) > 0)
2414 create_new_tab(url
, NULL
, 1, args
->precount
);
2416 create_new_tab(NULL
, NULL
, 1, args
->precount
);
2419 if (args
->precount
< 0)
2422 TAILQ_FOREACH(tt
, &tabs
, entry
)
2423 if (tt
->tab_id
== args
->precount
- 1) {
2428 case XT_TAB_DELQUIT
:
2429 if (gtk_notebook_get_n_pages(notebook
) > 1)
2435 if (strlen(url
) > 0)
2438 rv
= XT_CB_PASSTHROUGH
;
2444 if (show_tabs
== 0) {
2446 notebook_tab_set_visibility();
2450 if (show_tabs
== 1) {
2452 notebook_tab_set_visibility();
2455 case XT_TAB_NEXTSTYLE
:
2456 if (tab_style
== XT_TABS_NORMAL
) {
2457 tab_style
= XT_TABS_COMPACT
;
2458 recolor_compact_tabs();
2461 tab_style
= XT_TABS_NORMAL
;
2462 notebook_tab_set_visibility();
2464 case XT_TAB_UNDO_CLOSE
:
2465 if (undo_count
== 0) {
2466 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
2471 u
= TAILQ_FIRST(&undos
);
2472 create_new_tab(u
->uri
, u
, 1, -1);
2474 TAILQ_REMOVE(&undos
, u
, entry
);
2476 /* u->history is freed in create_new_tab() */
2480 case XT_TAB_LOAD_IMAGES
:
2482 if (!auto_load_images
) {
2484 /* Enable auto-load images (this will load all
2485 * previously unloaded images). */
2486 g_object_set(G_OBJECT(t
->settings
),
2487 "auto-load-images", TRUE
, (char *)NULL
);
2488 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2490 webkit_web_view_reload(t
->wv
);
2492 /* Webkit triggers an event when we change the setting,
2493 * so we can't disable the auto-loading at once.
2495 * Unfortunately, webkit does not tell us when it's done.
2496 * Instead, we wait until the next request, and then
2497 * disable autoloading again.
2499 t
->load_images
= TRUE
;
2503 rv
= XT_CB_PASSTHROUGH
;
2517 resizetab(struct tab
*t
, struct karg
*args
)
2519 if (t
== NULL
|| args
== NULL
) {
2520 show_oops(NULL
, "resizetab invalid parameters");
2521 return (XT_CB_PASSTHROUGH
);
2524 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
2525 t
->tab_id
, args
->i
);
2527 setzoom_webkit(t
, args
->i
);
2529 return (XT_CB_HANDLED
);
2533 movetab(struct tab
*t
, struct karg
*args
)
2537 if (t
== NULL
|| args
== NULL
) {
2538 show_oops(NULL
, "movetab invalid parameters");
2539 return (XT_CB_PASSTHROUGH
);
2542 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
2543 t
->tab_id
, args
->i
);
2545 if (args
->i
>= XT_TAB_INVALID
)
2546 return (XT_CB_PASSTHROUGH
);
2548 if (TAILQ_EMPTY(&tabs
))
2549 return (XT_CB_PASSTHROUGH
);
2551 n
= gtk_notebook_get_n_pages(notebook
);
2552 dest
= gtk_notebook_get_current_page(notebook
);
2556 if (args
->precount
< 0)
2557 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
2559 dest
= args
->precount
- 1;
2563 if (args
->precount
< 0)
2566 dest
-= args
->precount
% n
;
2579 return (XT_CB_PASSTHROUGH
);
2582 if (dest
< 0 || dest
>= n
)
2583 return (XT_CB_PASSTHROUGH
);
2584 if (t
->tab_id
== dest
) {
2585 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
2586 return (XT_CB_HANDLED
);
2589 set_current_tab(dest
);
2591 return (XT_CB_HANDLED
);
2598 command(struct tab
*t
, struct karg
*args
)
2600 char *s
= NULL
, *ss
= NULL
;
2605 if (t
== NULL
|| args
== NULL
) {
2606 show_oops(NULL
, "command invalid parameters");
2607 return (XT_CB_PASSTHROUGH
);
2618 if (cmd_prefix
== 0)
2621 ss
= g_strdup_printf(":%d", cmd_prefix
);
2627 t
->mode
= XT_MODE_HINT
;
2628 bzero(&a
, sizeof a
);
2634 t
->mode
= XT_MODE_HINT
;
2635 bzero(&a
, sizeof a
);
2636 a
.i
= XT_HINT_NEWTAB
;
2646 case XT_CMD_OPEN_CURRENT
:
2649 case XT_CMD_TABNEW_CURRENT
:
2650 if (!s
) /* FALL THROUGH? */
2652 if ((uri
= get_uri(t
)) != NULL
) {
2653 ss
= g_strdup_printf("%s%s", s
, uri
);
2658 show_oops(t
, "command: invalid opcode %d", args
->i
);
2659 return (XT_CB_PASSTHROUGH
);
2662 DNPRINTF(XT_D_CMD
, "%s: tab %d type %s\n", __func__
, t
->tab_id
, s
);
2664 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
2665 text
= gdk_color_to_string(&t
->default_style
->text
[GTK_STATE_NORMAL
]);
2666 base
= gdk_color_to_string(&t
->default_style
->base
[GTK_STATE_NORMAL
]);
2667 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
,
2668 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
2669 statusbar_modify_attr(t
, text
, base
);
2673 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
2674 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
2679 return (XT_CB_HANDLED
);
2683 search(struct tab
*t
, struct karg
*args
)
2687 if (t
== NULL
|| args
== NULL
) {
2688 show_oops(NULL
, "search invalid parameters");
2693 case XT_SEARCH_NEXT
:
2694 d
= t
->search_forward
;
2696 case XT_SEARCH_PREV
:
2697 d
= !t
->search_forward
;
2700 return (XT_CB_PASSTHROUGH
);
2703 if (t
->search_text
== NULL
) {
2704 if (global_search
== NULL
)
2705 return (XT_CB_PASSTHROUGH
);
2707 d
= t
->search_forward
= TRUE
;
2708 t
->search_text
= g_strdup(global_search
);
2709 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
2710 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
2714 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
2715 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
2717 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
2719 return (XT_CB_HANDLED
);
2723 session_save(struct tab
*t
, char *filename
)
2729 if (strlen(filename
) == 0)
2732 if (filename
[0] == '.' || filename
[0] == '/')
2736 if (save_tabs(t
, &a
))
2738 strlcpy(named_session
, filename
, sizeof named_session
);
2740 /* add the new session to the list of sessions */
2741 s
= g_malloc(sizeof(struct session
));
2742 s
->name
= g_strdup(filename
);
2743 TAILQ_INSERT_TAIL(&sessions
, s
, entry
);
2751 session_open(struct tab
*t
, char *filename
)
2756 if (strlen(filename
) == 0)
2759 if (filename
[0] == '.' || filename
[0] == '/')
2763 a
.i
= XT_SES_CLOSETABS
;
2764 if (open_tabs(t
, &a
))
2767 strlcpy(named_session
, filename
, sizeof named_session
);
2775 session_delete(struct tab
*t
, char *filename
)
2777 char file
[PATH_MAX
];
2781 if (strlen(filename
) == 0)
2784 if (filename
[0] == '.' || filename
[0] == '/')
2787 snprintf(file
, sizeof file
, "%s" PS
"%s", sessions_dir
, filename
);
2791 if (!strcmp(filename
, named_session
))
2792 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
2793 sizeof named_session
);
2795 /* remove session from sessions list */
2796 TAILQ_FOREACH(s
, &sessions
, entry
) {
2797 if (!strcmp(s
->name
, filename
))
2802 TAILQ_REMOVE(&sessions
, s
, entry
);
2803 g_free((gpointer
) s
->name
);
2812 session_cmd(struct tab
*t
, struct karg
*args
)
2814 char *filename
= args
->s
;
2819 if (args
->i
& XT_SHOW
)
2820 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
2821 XT_SAVED_TABS_FILE
: named_session
);
2822 else if (args
->i
& XT_SAVE
) {
2823 if (session_save(t
, filename
)) {
2824 show_oops(t
, "Can't save session: %s",
2825 filename
? filename
: "INVALID");
2828 } else if (args
->i
& XT_OPEN
) {
2829 if (session_open(t
, filename
)) {
2830 show_oops(t
, "Can't open session: %s",
2831 filename
? filename
: "INVALID");
2834 } else if (args
->i
& XT_DELETE
) {
2835 if (session_delete(t
, filename
)) {
2836 show_oops(t
, "Can't delete session: %s",
2837 filename
? filename
: "INVALID");
2842 return (XT_CB_PASSTHROUGH
);
2846 script_cmd(struct tab
*t
, struct karg
*args
)
2855 if ((f
= fopen(args
->s
, "r")) == NULL
) {
2856 show_oops(t
, "Can't open script file: %s", args
->s
);
2860 if (fstat(fileno(f
), &sb
) == -1) {
2861 show_oops(t
, "Can't stat script file: %s", args
->s
);
2865 buf
= g_malloc0(sb
.st_size
+ 1);
2866 if (fread(buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2867 show_oops(t
, "Can't read script file: %s", args
->s
);
2871 DNPRINTF(XT_D_JS
, "%s: about to run script\n", __func__
);
2880 return (XT_CB_PASSTHROUGH
);
2884 * Make a hardcopy of the page
2887 print_page(struct tab
*t
, struct karg
*args
)
2889 WebKitWebFrame
*frame
;
2891 GtkPrintOperation
*op
;
2892 GtkPrintOperationAction action
;
2893 GtkPrintOperationResult print_res
;
2894 GError
*g_err
= NULL
;
2895 int marg_l
, marg_r
, marg_t
, marg_b
;
2897 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
2899 ps
= gtk_page_setup_new();
2900 op
= gtk_print_operation_new();
2901 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
2902 frame
= webkit_web_view_get_main_frame(t
->wv
);
2904 /* the default margins are too small, so we will bump them */
2905 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
2906 XT_PRINT_EXTRA_MARGIN
;
2907 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
2908 XT_PRINT_EXTRA_MARGIN
;
2909 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
2910 XT_PRINT_EXTRA_MARGIN
;
2911 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
2912 XT_PRINT_EXTRA_MARGIN
;
2915 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
2916 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
2917 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
2918 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
2920 gtk_print_operation_set_default_page_setup(op
, ps
);
2922 /* this appears to free 'op' and 'ps' */
2923 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
2925 /* check it worked */
2926 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
2927 show_oops(NULL
, "can't print: %s", g_err
->message
);
2928 g_error_free (g_err
);
2936 go_home(struct tab
*t
, struct karg
*args
)
2943 set_encoding(struct tab
*t
, struct karg
*args
)
2947 if (args
->s
&& strlen(g_strstrip(args
->s
)) == 0) {
2948 e
= webkit_web_view_get_custom_encoding(t
->wv
);
2950 e
= webkit_web_view_get_encoding(t
->wv
);
2951 show_oops(t
, "encoding: %s", e
? e
: "N/A");
2953 webkit_web_view_set_custom_encoding(t
->wv
, args
->s
);
2959 restart(struct tab
*t
, struct karg
*args
)
2963 a
.s
= XT_RESTART_TABS_FILE
;
2965 execvp(start_argv
[0], start_argv
);
2971 char *http_proxy_save
; /* not a setting, used to toggle */
2974 proxy_cmd(struct tab
*t
, struct karg
*args
)
2976 DNPRINTF(XT_D_CMD
, "%s: tab %d\n", __func__
, t
->tab_id
);
2983 if (http_proxy_save
)
2984 g_free(http_proxy_save
);
2985 http_proxy_save
= g_strdup(http_proxy
);
2988 if (args
->i
& XT_PRXY_SHOW
) {
2990 show_oops(t
, "http_proxy = %s", http_proxy
);
2992 show_oops(t
, "proxy is currently disabled");
2993 } else if (args
->i
& XT_PRXY_TOGGLE
) {
2994 if (http_proxy_save
== NULL
&& http_proxy
== NULL
) {
2995 show_oops(t
, "can't toggle proxy");
3001 setup_proxy(http_proxy_save
);
3004 return (XT_CB_PASSTHROUGH
);
3009 int (*func
)(struct tab
*, struct karg
*);
3013 { "command_mode", 0, command_mode
, XT_MODE_COMMAND
, 0 },
3014 { "insert_mode", 0, command_mode
, XT_MODE_INSERT
, 0 },
3015 { "command", 0, command
, ':', 0 },
3016 { "search", 0, command
, '/', 0 },
3017 { "searchb", 0, command
, '?', 0 },
3018 { "hinting", 0, command
, '.', 0 },
3019 { "hinting_newtab", 0, command
, ',', 0 },
3020 { "togglesrc", 0, toggle_src
, 0, 0 },
3021 { "editsrc", 0, edit_src
, 0, 0 },
3022 { "editelement", 0, edit_element
, 0, 0 },
3023 { "passthrough", 0, passthrough
, 0, 0 },
3024 { "modurl", 0, modurl
, 0, 0 },
3026 /* yanking and pasting */
3027 { "yankuri", 0, yank_uri
, 0, 0 },
3028 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
3029 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
3030 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
3033 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
3034 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
3037 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
3038 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
3041 { "hinting", 0, hint
, 0, 0 },
3042 { "hinting_newtab", 0, hint
, XT_HINT_NEWTAB
, 0 },
3044 /* custom stylesheet */
3045 { "userstyle", 0, userstyle
, XT_STYLE_CURRENT_TAB
, 0 },
3046 { "userstyle_global", 0, userstyle
, XT_STYLE_GLOBAL
, 0 },
3049 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
3050 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
3051 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
3052 { "stop", 0, navaction
, XT_NAV_STOP
, 0 },
3054 /* vertical movement */
3055 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
3056 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
3057 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
3058 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
3059 { "1", 0, move
, XT_MOVE_TOP
, 0 },
3060 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
3061 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
3062 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
3063 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
3064 /* horizontal movement */
3065 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
3066 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
3067 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
3068 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
3070 { "favorites", 0, xtp_page_fl
, 0, 0 },
3071 { "fav", 0, xtp_page_fl
, 0, 0 },
3072 { "favadd", 0, add_favorite
, 0, 0 },
3074 { "qall", 0, quit
, 0, 0 },
3075 { "quitall", 0, quit
, 0, 0 },
3076 { "w", 0, save_tabs
, 0, 0 },
3077 { "wq", 0, save_tabs_and_quit
, 0, 0 },
3078 { "help", 0, help
, 0, 0 },
3079 { "about", 0, about
, 0, 0 },
3080 { "stats", 0, stats
, 0, 0 },
3081 { "version", 0, about
, 0, 0 },
3084 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3085 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
3086 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
3087 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
3088 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3089 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3090 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
3091 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
3092 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
3093 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
3094 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
3096 /* cookie command */
3097 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3098 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
3099 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
3100 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
3101 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3102 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3103 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
3104 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
3105 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
3106 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
3107 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
3108 { "purge", 1, cookie_cmd
, XT_DELETE
, 0 },
3110 /* plugin command */
3111 { "plugin", 0, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3112 { "save", 1, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
3113 { "domain", 2, pl_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
3114 { "fqdn", 2, pl_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
3115 { "show", 1, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3116 { "all", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
3117 { "persistent", 2, pl_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
3118 { "session", 2, pl_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
3119 { "toggle", 1, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
3120 { "domain", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
3121 { "fqdn", 2, pl_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
3123 /* toplevel (domain) command */
3124 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
3125 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
3128 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
3131 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
3132 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
3133 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
3135 { "ca", 0, ca_cmd
, 0, 0 },
3136 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
3137 { "dl", 0, xtp_page_dl
, 0, 0 },
3138 { "h", 0, xtp_page_hl
, 0, 0 },
3139 { "history", 0, xtp_page_hl
, 0, 0 },
3140 { "home", 0, go_home
, 0, 0 },
3141 { "restart", 0, restart
, 0, 0 },
3142 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
3143 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
3144 { "statustoggle", 0, statustoggle
, 0, 0 },
3145 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
3147 { "print", 0, print_page
, 0, 0 },
3150 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
3151 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
3152 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
3153 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
3154 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
3155 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
3156 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
3157 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
3158 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
3159 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
3160 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
3161 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
3162 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
3163 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
3164 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
3165 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
3166 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
3167 { "tabs", 0, buffers
, 0, 0 },
3168 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
3169 { "buffers", 0, buffers
, 0, 0 },
3170 { "ls", 0, buffers
, 0, 0 },
3171 { "encoding", 0, set_encoding
, 0, XT_USERARG
},
3172 { "loadimages", 0, tabaction
, XT_TAB_LOAD_IMAGES
, 0 },
3174 /* command aliases (handy when -S flag is used) */
3175 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
3176 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
3177 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
3178 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
3181 { "set", 0, set
, 0, XT_SETARG
},
3183 { "fullscreen", 0, fullscreen
, 0, 0 },
3184 { "f", 0, fullscreen
, 0, 0 },
3187 { "session", 0, session_cmd
, XT_SHOW
, 0 },
3188 { "delete", 1, session_cmd
, XT_DELETE
, XT_SESSARG
},
3189 { "open", 1, session_cmd
, XT_OPEN
, XT_SESSARG
},
3190 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
3191 { "show", 1, session_cmd
, XT_SHOW
, 0 },
3193 /* external javascript */
3194 { "script", 0, script_cmd
, XT_EJS_SHOW
, XT_USERARG
},
3197 { "inspector", 0, inspector_cmd
, XT_INS_SHOW
, 0 },
3198 { "show", 1, inspector_cmd
, XT_INS_SHOW
, 0 },
3199 { "hide", 1, inspector_cmd
, XT_INS_HIDE
, 0 },
3202 { "proxy", 0, proxy_cmd
, XT_PRXY_SHOW
, 0 },
3203 { "show", 1, proxy_cmd
, XT_PRXY_SHOW
, 0 },
3204 { "toggle", 1, proxy_cmd
, XT_PRXY_TOGGLE
, 0 },
3211 } cmd_status
= {-1, 0};
3214 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
3217 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
3224 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
3227 WebKitHitTestResult
*hit_test_result
;
3230 hit_test_result
= webkit_web_view_get_hit_test_result(t
->wv
, e
);
3231 g_object_get(hit_test_result
, "context", &context
, NULL
);
3236 if (context
& WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE
)
3237 t
->mode
= XT_MODE_INSERT
;
3239 t
->mode
= XT_MODE_COMMAND
;
3241 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
3243 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
3249 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
3251 a
.i
= XT_NAV_FORWARD
;
3261 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
3263 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
3265 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
3274 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
3276 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
3278 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
3281 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
3286 show_oops(t
, "activate_uri_entry_cb no uri");
3290 uri
+= strspn(uri
, "\t ");
3292 /* if xxxt:// treat specially */
3293 if (parse_xtp_url(t
, uri
))
3296 /* otherwise continue to load page normally */
3297 load_uri(t
, (gchar
*)uri
);
3302 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
3304 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
3305 char *newuri
= NULL
;
3308 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
3311 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
3315 if (search_string
== NULL
) {
3316 show_oops(t
, "no search_string");
3320 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3322 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
3323 newuri
= g_strdup_printf(search_string
, enc_search
);
3327 webkit_web_view_load_uri(t
->wv
, newuri
);
3335 check_and_set_cookie(const gchar
*uri
, struct tab
*t
)
3337 struct domain
*d
= NULL
;
3340 if (uri
== NULL
|| t
== NULL
)
3343 if ((d
= wl_find_uri(uri
, &c_wl
)) == NULL
)
3348 DNPRINTF(XT_D_COOKIE
, "check_and_set_cookie: %s %s\n",
3349 es
? "enable" : "disable", uri
);
3351 g_object_set(G_OBJECT(t
->settings
),
3352 "enable-html5-local-storage", es
, (char *)NULL
);
3353 webkit_web_view_set_settings(t
->wv
, t
->settings
);
3357 check_and_set_js(const gchar
*uri
, struct tab
*t
)
3359 struct domain
*d
= NULL
;
3362 if (uri
== NULL
|| t
== NULL
)
3365 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
3370 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
3371 es
? "enable" : "disable", uri
);
3373 g_object_set(G_OBJECT(t
->settings
),
3374 "enable-scripts", es
, (char *)NULL
);
3375 g_object_set(G_OBJECT(t
->settings
),
3376 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
3377 webkit_web_view_set_settings(t
->wv
, t
->settings
);
3379 button_set_stockid(t
->js_toggle
,
3380 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
3384 check_and_set_pl(const gchar
*uri
, struct tab
*t
)
3386 struct domain
*d
= NULL
;
3389 if (uri
== NULL
|| t
== NULL
)
3392 if ((d
= wl_find_uri(uri
, &pl_wl
)) == NULL
)
3397 DNPRINTF(XT_D_JS
, "check_and_set_pl: %s %s\n",
3398 es
? "enable" : "disable", uri
);
3400 g_object_set(G_OBJECT(t
->settings
),
3401 "enable-plugins", es
, (char *)NULL
);
3402 webkit_web_view_set_settings(t
->wv
, t
->settings
);
3406 color_address_bar(gpointer p
)
3409 struct tab
*tt
, *t
= p
;
3410 gchar
*col_str
= XT_COLOR_WHITE
, *text
, *base
;
3411 const gchar
*uri
, *u
= NULL
, *error_str
= NULL
;
3414 gdk_threads_enter();
3416 DNPRINTF(XT_D_URL
, "%s:\n", __func__
);
3418 /* make sure t still exists */
3421 TAILQ_FOREACH(tt
, &tabs
, entry
)
3427 if ((uri
= get_uri(t
)) == NULL
)
3432 gdk_threads_leave();
3435 col_str
= XT_COLOR_YELLOW
;
3436 switch (load_compare_cert(u
, &error_str
)) {
3438 col_str
= XT_COLOR_BLUE
;
3441 col_str
= XT_COLOR_GREEN
;
3443 case CERT_UNTRUSTED
:
3444 col_str
= XT_COLOR_YELLOW
;
3447 col_str
= XT_COLOR_RED
;
3452 gdk_threads_enter();
3454 /* make sure t isn't deleted */
3455 TAILQ_FOREACH(tt
, &tabs
, entry
)
3462 /* test to see if the user navigated away and canceled the thread */
3463 if (t
->thread
!= g_thread_self())
3465 if ((uri
= get_uri(t
)) == NULL
) {
3469 if (strcmp(uri
, u
)) {
3470 /* make sure we are still the same url */
3477 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
3478 text
= gdk_color_to_string(
3479 &t
->default_style
->text
[GTK_STATE_NORMAL
]);
3480 base
= gdk_color_to_string(
3481 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
3482 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
,
3483 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
3484 statusbar_modify_attr(t
, text
, base
);
3488 gdk_color_parse(col_str
, &color
);
3489 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3490 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
3493 if (error_str
&& error_str
[0] != '\0')
3494 show_oops(t
, "%s", error_str
);
3499 /* t is invalid at this point */
3501 g_free((gpointer
)u
);
3503 gdk_threads_leave();
3508 show_ca_status(struct tab
*t
, const char *uri
)
3511 gchar
*col_str
= XT_COLOR_WHITE
, *text
, *base
;
3513 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
3514 ssl_strict_certs
, ssl_ca_file
, uri
);
3521 if (ssl_ca_file
== NULL
) {
3522 if (g_str_has_prefix(uri
, "http://"))
3524 if (g_str_has_prefix(uri
, "https://")) {
3525 col_str
= XT_COLOR_RED
;
3530 if (g_str_has_prefix(uri
, "http://") ||
3531 !g_str_has_prefix(uri
, "https://"))
3535 * It is not necessary to see if the thread is already running.
3536 * If the thread is in progress setting it to something else aborts it
3540 /* thread the coloring of the address bar */
3541 t
->thread
= g_thread_create((GThreadFunc
)color_address_bar
, t
, TRUE
, NULL
);
3543 color_address_bar(t
);
3550 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
3551 text
= gdk_color_to_string(
3552 &t
->default_style
->text
[GTK_STATE_NORMAL
]);
3553 base
= gdk_color_to_string(
3554 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
3555 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
,
3556 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
3557 statusbar_modify_attr(t
, text
, base
);
3561 gdk_color_parse(col_str
, &color
);
3562 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3563 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
3569 free_favicon(struct tab
*t
)
3571 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
3572 __func__
, t
->icon_download
, t
->icon_request
);
3574 if (t
->icon_request
)
3575 g_object_unref(t
->icon_request
);
3576 if (t
->icon_dest_uri
)
3577 g_free(t
->icon_dest_uri
);
3579 t
->icon_request
= NULL
;
3580 t
->icon_dest_uri
= NULL
;
3584 xt_icon_from_name(struct tab
*t
, gchar
*name
)
3586 if (!enable_favicon_entry
)
3589 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
3590 GTK_ENTRY_ICON_PRIMARY
, "text-html");
3592 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3593 GTK_ENTRY_ICON_PRIMARY
, "text-html");
3595 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3596 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3600 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
3602 GdkPixbuf
*pb_scaled
;
3604 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
3605 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
3606 GDK_INTERP_BILINEAR
);
3610 if (enable_favicon_entry
) {
3613 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
3614 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
3617 if (show_url
== 0) {
3618 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
3619 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
3621 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3622 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3625 /* XXX: Only supports the minimal tabs atm. */
3626 if (enable_favicon_tabs
)
3627 gtk_image_set_from_pixbuf(GTK_IMAGE(t
->tab_elems
.favicon
),
3630 if (pb_scaled
!= pb
)
3631 g_object_unref(pb_scaled
);
3635 xt_icon_from_file(struct tab
*t
, char *file
)
3639 if (g_str_has_prefix(file
, "file://"))
3640 file
+= strlen("file://");
3642 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
3644 xt_icon_from_pixbuf(t
, pb
);
3647 xt_icon_from_name(t
, "text-html");
3651 is_valid_icon(char *file
)
3654 const char *mime_type
;
3658 gf
= g_file_new_for_path(file
);
3659 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
3661 mime_type
= g_file_info_get_content_type(fi
);
3662 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
3663 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
3664 g_strcmp0(mime_type
, "image/png") == 0 ||
3665 g_strcmp0(mime_type
, "image/gif") == 0 ||
3666 g_strcmp0(mime_type
, "application/octet-stream") == 0;
3674 set_favicon_from_file(struct tab
*t
, char *file
)
3678 if (t
== NULL
|| file
== NULL
)
3681 if (g_str_has_prefix(file
, "file://"))
3682 file
+= strlen("file://");
3683 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
3685 if (!stat(file
, &sb
)) {
3686 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
3687 /* corrupt icon so trash it */
3688 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
3691 /* no need to set icon to default here */
3695 xt_icon_from_file(t
, file
);
3699 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
3702 WebKitDownloadStatus status
= webkit_download_get_status(download
);
3703 struct tab
*tt
= NULL
, *t
= NULL
;
3706 * find the webview instead of passing in the tab as it could have been
3707 * deleted from underneath us.
3709 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3718 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
3719 __func__
, t
->tab_id
, status
);
3722 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3724 t
->icon_download
= NULL
;
3727 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3730 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3733 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3735 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
3736 __func__
, t
->tab_id
);
3737 t
->icon_download
= NULL
;
3740 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3743 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
3744 __func__
, t
->icon_dest_uri
);
3745 set_favicon_from_file(t
, t
->icon_dest_uri
);
3746 /* these will be freed post callback */
3747 t
->icon_request
= NULL
;
3748 t
->icon_download
= NULL
;
3756 abort_favicon_download(struct tab
*t
)
3758 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
3760 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
3761 if (t
->icon_download
) {
3762 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
3763 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
3764 webkit_download_cancel(t
->icon_download
);
3765 t
->icon_download
= NULL
;
3770 xt_icon_from_name(t
, "text-html");
3774 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
3776 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
3778 if (uri
== NULL
|| t
== NULL
)
3781 #if WEBKIT_CHECK_VERSION(1, 4, 0)
3782 /* take icon from WebKitIconDatabase */
3785 pb
= webkit_web_view_get_icon_pixbuf(wv
);
3787 xt_icon_from_pixbuf(t
, pb
);
3790 xt_icon_from_name(t
, "text-html");
3791 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
3792 /* download icon to cache dir */
3793 gchar
*name_hash
, file
[PATH_MAX
];
3796 if (t
->icon_request
) {
3797 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
3801 /* check to see if we got the icon in cache */
3802 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
3803 snprintf(file
, sizeof file
, "%s" PS
"%s.ico", cache_dir
, name_hash
);
3806 if (!stat(file
, &sb
)) {
3807 if (sb
.st_size
> 0) {
3808 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
3810 set_favicon_from_file(t
, file
);
3814 /* corrupt icon so trash it */
3815 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
3820 /* create download for icon */
3821 t
->icon_request
= webkit_network_request_new(uri
);
3822 if (t
->icon_request
== NULL
) {
3823 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
3828 t
->icon_download
= webkit_download_new(t
->icon_request
);
3829 if (t
->icon_download
== NULL
)
3832 /* we have to free icon_dest_uri later */
3833 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
3834 webkit_download_set_destination_uri(t
->icon_download
,
3837 if (webkit_download_get_status(t
->icon_download
) ==
3838 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
3839 g_object_unref(t
->icon_request
);
3840 g_free(t
->icon_dest_uri
);
3841 t
->icon_request
= NULL
;
3842 t
->icon_dest_uri
= NULL
;
3846 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
3847 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
3849 webkit_download_start(t
->icon_download
);
3854 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
3856 const gchar
*uri
= NULL
;
3857 struct history
*h
, find
;
3861 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
3862 webkit_web_view_get_load_status(wview
),
3863 get_uri(t
) ? get_uri(t
) : "NOTHING");
3866 show_oops(NULL
, "notify_load_status_cb invalid parameters");
3870 switch (webkit_web_view_get_load_status(wview
)) {
3871 case WEBKIT_LOAD_PROVISIONAL
:
3873 abort_favicon_download(t
);
3874 #if GTK_CHECK_VERSION(2, 20, 0)
3875 gtk_widget_show(t
->spinner
);
3876 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
3878 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
3880 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
3882 /* assume we are a new address */
3883 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
,
3884 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
3885 text
= gdk_color_to_string(
3886 &t
->default_style
->text
[GTK_STATE_NORMAL
]);
3887 base
= gdk_color_to_string(
3888 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
3889 statusbar_modify_attr(t
, text
, base
);
3893 /* take focus if we are visible */
3899 /* kill color thread */
3904 case WEBKIT_LOAD_COMMITTED
:
3909 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
3915 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
3917 /* check if js white listing is enabled */
3918 if (enable_plugin_whitelist
)
3919 check_and_set_pl(uri
, t
);
3920 if (enable_cookie_whitelist
)
3921 check_and_set_cookie(uri
, t
);
3922 if (enable_js_whitelist
)
3923 check_and_set_js(uri
, t
);
3929 /* we know enough to autosave the session */
3930 if (session_autosave
) {
3935 show_ca_status(t
, uri
);
3936 run_script(t
, JS_HINTING
);
3937 if (enable_autoscroll
)
3938 run_script(t
, JS_AUTOSCROLL
);
3941 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
3943 if (color_visited_uris
) {
3944 color_visited(t
, color_visited_helper());
3946 /* This colors the links you middle-click (open in new
3947 * tab) in the current tab. */
3948 if (t
->tab_id
!= gtk_notebook_get_current_page(notebook
) &&
3949 (uri
= get_uri(t
)) != NULL
)
3950 color_visited(get_current_tab(),
3951 g_strdup_printf("{'%s' : 'dummy'}", uri
));
3955 case WEBKIT_LOAD_FINISHED
:
3957 if ((uri
= get_uri(t
)) == NULL
)
3960 if (!strncmp(uri
, "http://", strlen("http://")) ||
3961 !strncmp(uri
, "https://", strlen("https://")) ||
3962 !strncmp(uri
, "file://", strlen("file://"))) {
3963 find
.uri
= (gchar
*)uri
;
3964 h
= RB_FIND(history_list
, &hl
, &find
);
3966 insert_history_item(uri
,
3967 get_title(t
, FALSE
), time(NULL
));
3969 h
->time
= time(NULL
);
3972 set_status(t
, (char *)uri
, XT_STATUS_URI
);
3973 #if WEBKIT_CHECK_VERSION(1, 1, 18)
3974 case WEBKIT_LOAD_FAILED
:
3977 #if GTK_CHECK_VERSION(2, 20, 0)
3978 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
3979 gtk_widget_hide(t
->spinner
);
3982 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
3987 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
3989 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
3990 can_go_back_for_real(t
));
3992 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
3993 can_go_forward_for_real(t
));
3997 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
3999 const gchar
*title
= NULL
, *win_title
= NULL
;
4001 title
= get_title(t
, FALSE
);
4002 win_title
= get_title(t
, TRUE
);
4004 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
4005 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
4008 if (win_title
&& t
->tab_id
== gtk_notebook_get_current_page(notebook
))
4009 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
4013 get_domain(const gchar
*host
)
4018 /* handle silly domains like .co.uk */
4020 if ((x
= strlen(host
)) <= 6)
4021 return (g_strdup(host
));
4023 if (host
[x
- 3] == '.' && host
[x
- 6] == '.') {
4029 return (g_strdup(&host
[x
+ 1]));
4033 p
= g_strrstr(host
, ".");
4035 return (g_strdup(""));
4041 return (g_strdup(p
+ 1));
4043 return (g_strdup(host
));
4047 js_autorun(struct tab
*t
)
4051 size_t got_default
= 0, got_host
= 0;
4053 char deff
[PATH_MAX
], hostf
[PATH_MAX
];
4054 char *js
= NULL
, *jsat
, *domain
= NULL
;
4055 FILE *deffile
= NULL
, *hostfile
= NULL
;
4057 if (js_autorun_enabled
== 0)
4062 !(g_str_has_prefix(uri
, "http://") ||
4063 g_str_has_prefix(uri
, "https://")))
4066 su
= soup_uri_new(uri
);
4069 if (!SOUP_URI_VALID_FOR_HTTP(su
))
4072 DNPRINTF(XT_D_JS
, "%s: host: %s domain: %s\n", __func__
,
4074 domain
= get_domain(su
->host
);
4076 snprintf(deff
, sizeof deff
, "%s" PS
"default.js", js_dir
);
4077 if ((deffile
= fopen(deff
, "r")) != NULL
) {
4078 if (fstat(fileno(deffile
), &sb
) == -1) {
4079 show_oops(t
, "can't stat default JS file");
4082 got_default
= sb
.st_size
;
4085 /* try host first followed by domain */
4086 snprintf(hostf
, sizeof hostf
, "%s" PS
"%s.js", js_dir
, su
->host
);
4087 DNPRINTF(XT_D_JS
, "trying file: %s\n", hostf
);
4088 if ((hostfile
= fopen(hostf
, "r")) == NULL
) {
4089 snprintf(hostf
, sizeof hostf
, "%s" PS
"%s.js", js_dir
, domain
);
4090 DNPRINTF(XT_D_JS
, "trying file: %s\n", hostf
);
4091 if ((hostfile
= fopen(hostf
, "r")) == NULL
)
4094 DNPRINTF(XT_D_JS
, "file: %s\n", hostf
);
4095 if (fstat(fileno(hostfile
), &sb
) == -1) {
4096 show_oops(t
, "can't stat %s JS file", hostf
);
4099 got_host
= sb
.st_size
;
4102 if (got_default
+ got_host
== 0)
4105 js
= g_malloc0(got_default
+ got_host
+ 1);
4109 if (fread(js
, got_default
, 1, deffile
) != 1) {
4110 show_oops(t
, "default file read error");
4113 jsat
= js
+ got_default
;
4117 if (fread(jsat
, got_host
, 1, hostfile
) != 1) {
4118 show_oops(t
, "host file read error");
4123 DNPRINTF(XT_D_JS
, "%s: about to run script\n", __func__
);
4140 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4142 /* autorun some js if enabled */
4150 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
4152 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
4153 progress
== 100 ? 0 : (double)progress
/ 100);
4154 if (show_url
== 0) {
4155 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
4156 progress
== 100 ? 0 : (double)progress
/ 100);
4159 update_statusbar_position(NULL
, NULL
);
4163 strict_transport_rb_cmp(struct strict_transport
*a
, struct strict_transport
*b
)
4168 /* compare strings from the end */
4169 l1
= strlen(a
->host
);
4170 l2
= strlen(b
->host
);
4174 for (; *p1
== *p2
&& p1
> a
->host
&& p2
> b
->host
;
4179 * Check if we need to do pattern expansion,
4180 * or if we're just keeping the tree in order
4182 if (a
->flags
& XT_STS_FLAGS_EXPAND
&&
4183 b
->flags
& XT_STS_FLAGS_INCLUDE_SUBDOMAINS
) {
4184 /* Check if we're matching the
4185 * 'host.xyz' part in '*.host.xyz'
4187 if (p2
== b
->host
&& (p1
== a
->host
|| *(p1
-1) == '.')) {
4192 if (p1
== a
->host
&& p2
== b
->host
)
4206 RB_GENERATE(strict_transport_tree
, strict_transport
, entry
,
4207 strict_transport_rb_cmp
);
4210 strict_transport_add(const char *domain
, time_t timeout
, int subdomains
)
4212 struct strict_transport
*d
, find
;
4216 if (enable_strict_transport
== FALSE
)
4219 DPRINTF("strict_transport_add(%s,%lld,%d)\n", domain
,
4220 (long long)timeout
, subdomains
);
4226 find
.host
= (char *)domain
;
4228 d
= RB_FIND(strict_transport_tree
, &st_tree
, &find
);
4232 /* check if update is needed */
4233 if (d
->timeout
== timeout
&&
4234 (d
->flags
& XT_STS_FLAGS_INCLUDE_SUBDOMAINS
) == subdomains
)
4237 d
->timeout
= timeout
;
4239 d
->flags
|= XT_STS_FLAGS_INCLUDE_SUBDOMAINS
;
4241 /* We're still initializing */
4242 if (strict_transport_file
== NULL
)
4245 if ((f
= fopen(strict_transport_file
, "w")) == NULL
) {
4247 "can't open strict-transport rules file");
4251 fprintf(f
, "# Generated file - do not update unless you know "
4252 "what you're doing\n");
4253 RB_FOREACH(d
, strict_transport_tree
, &st_tree
) {
4254 if (d
->timeout
< now
)
4256 fprintf(f
, "%s\t%lld\t%d\n", d
->host
, (long long)d
->timeout
,
4257 d
->flags
& XT_STS_FLAGS_INCLUDE_SUBDOMAINS
);
4261 d
= g_malloc(sizeof *d
);
4262 d
->host
= g_strdup(domain
);
4263 d
->timeout
= timeout
;
4265 d
->flags
= XT_STS_FLAGS_INCLUDE_SUBDOMAINS
;
4268 RB_INSERT(strict_transport_tree
, &st_tree
, d
);
4270 /* We're still initializing */
4271 if (strict_transport_file
== NULL
)
4274 if ((f
= fopen(strict_transport_file
, "a+")) == NULL
) {
4276 "can't open strict-transport rules file");
4280 fseek(f
, 0, SEEK_END
);
4281 fprintf(f
,"%s\t%lld\t%d\n", d
->host
, (long long)timeout
, subdomains
);
4288 strict_transport_check(const char *host
)
4290 static struct strict_transport
*d
= NULL
;
4291 struct strict_transport find
;
4293 if (enable_strict_transport
== FALSE
)
4296 find
.host
= (char *)host
;
4298 /* match for domains that include subdomains */
4299 find
.flags
= XT_STS_FLAGS_EXPAND
;
4301 /* First, check if we're already at the right node */
4302 if (d
!= NULL
&& strict_transport_rb_cmp(&find
, d
) == 0) {
4306 d
= RB_FIND(strict_transport_tree
, &st_tree
, &find
);
4314 strict_transport_init()
4316 char file
[PATH_MAX
];
4322 time_t timeout
, now
;
4325 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_STS_FILE
);
4326 if ((f
= fopen(file
, "r")) == NULL
) {
4327 strict_transport_file
= g_strdup(file
);
4338 if ((rule
= fparseln(f
, &len
, NULL
, delim
, 0)) == NULL
) {
4339 if (!feof(f
) || ferror(f
))
4345 /* get second entry */
4346 if ((ptr
= strpbrk(rule
, " \t")) == NULL
)
4350 timeout
= atoi(ptr
);
4352 /* get third entry */
4353 if ((ptr
= strpbrk(ptr
, " \t")) == NULL
)
4357 subdomains
= atoi(ptr
);
4360 strict_transport_add(rule
, timeout
, subdomains
);
4365 strict_transport_file
= g_strdup(file
);
4369 startpage_add("strict-transport rules file ('%s') is corrupt", file
);
4377 strict_transport_security_cb(SoupMessage
*msg
, gpointer data
)
4383 int subdomains
= FALSE
;
4388 sts
= soup_message_headers_get_one(msg
->response_headers
,
4389 "Strict-Transport-Security");
4390 uri
= soup_message_get_uri(msg
);
4392 if (sts
== NULL
|| uri
== NULL
)
4395 if ((ptr
= strcasestr(sts
, "max-age="))) {
4396 ptr
+= strlen("max-age=");
4397 timeout
= atoll(ptr
);
4399 return; /* malformed header - max-age must be included */
4401 if ((ptr
= strcasestr(sts
, "includeSubDomains")))
4404 strict_transport_add(uri
->host
, timeout
+ time(NULL
), subdomains
);
4408 session_rq_cb(SoupSession
*s
, SoupMessage
*msg
, SoupSocket
*socket
,
4418 if (s
== NULL
|| msg
== NULL
)
4421 if (referer_mode
== XT_REFERER_ALWAYS
)
4424 /* Check if referer is set - and what the user requested for referers */
4425 ref
= soup_message_headers_get_one(msg
->request_headers
, "Referer");
4427 DNPRINTF(XT_D_NAV
, "session_rq_cb: Referer: %s\n", ref
);
4428 switch (referer_mode
) {
4429 case XT_REFERER_NEVER
:
4430 DNPRINTF(XT_D_NAV
, "session_rq_cb: removing referer\n");
4431 soup_message_headers_remove(msg
->request_headers
,
4434 case XT_REFERER_SAME_DOMAIN
:
4435 ref_uri
= soup_uri_new(ref
);
4436 dest
= soup_message_get_uri(msg
);
4438 ref_suffix
= tld_get_suffix(ref_uri
->host
);
4439 dest_suffix
= tld_get_suffix(dest
->host
);
4441 if (dest
&& strcmp(ref_suffix
, dest_suffix
) != 0) {
4442 soup_message_headers_remove(msg
->request_headers
,
4444 DNPRINTF(XT_D_NAV
, "session_rq_cb: removing "
4445 "referer (not same domain) (suffixes: %s - %s)\n",
4446 ref_suffix
, dest_suffix
);
4448 soup_uri_free(ref_uri
);
4450 case XT_REFERER_SAME_FQDN
:
4451 ref_uri
= soup_uri_new(ref
);
4452 dest
= soup_message_get_uri(msg
);
4453 if (dest
&& strcmp(ref_uri
->host
, dest
->host
) != 0) {
4454 soup_message_headers_remove(msg
->request_headers
,
4456 DNPRINTF(XT_D_NAV
, "session_rq_cb: removing "
4457 "referer (not same fqdn) (should be %s)\n",
4460 soup_uri_free(ref_uri
);
4462 case XT_REFERER_CUSTOM
:
4463 DNPRINTF(XT_D_NAV
, "session_rq_cb: setting referer "
4464 "to %s\n", referer_custom
);
4465 soup_message_headers_replace(msg
->request_headers
,
4466 "Referer", referer_custom
);
4471 if (enable_strict_transport
) {
4472 soup_message_add_header_handler(msg
, "finished",
4473 "Strict-Transport-Security",
4474 G_CALLBACK(strict_transport_security_cb
), NULL
);
4479 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4480 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4481 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4484 WebKitWebNavigationReason reason
;
4485 struct domain
*d
= NULL
;
4488 show_oops(NULL
, "webview_npd_cb invalid parameters");
4492 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
4494 webkit_network_request_get_uri(request
));
4496 uri
= (char *)webkit_network_request_get_uri(request
);
4498 if (!auto_load_images
&& t
->load_images
) {
4500 /* Disable autoloading of images, now that we're done loading
4502 g_object_set(G_OBJECT(t
->settings
),
4503 "auto-load-images", FALSE
, (char *)NULL
);
4504 webkit_web_view_set_settings(t
->wv
, t
->settings
);
4506 t
->load_images
= FALSE
;
4509 /* If this is an xtp url, we don't load anything else. */
4510 if (parse_xtp_url(t
, uri
))
4513 if ((t
->mode
== XT_MODE_HINT
&& t
->new_tab
) || t
->ctrl_click
) {
4515 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
4516 webkit_web_policy_decision_ignore(pd
);
4517 return (TRUE
); /* we made the decission */
4520 /* Change user agent if more than one has been given. */
4521 if (user_agent_count
> 1) {
4522 struct user_agent
*ua
;
4524 if ((ua
= TAILQ_NEXT(user_agent
, entry
)) == NULL
)
4525 user_agent
= TAILQ_FIRST(&ua_list
);
4529 free(t
->user_agent
);
4530 t
->user_agent
= g_strdup(user_agent
->value
);
4532 DNPRINTF(XT_D_NAV
, "user-agent: %s\n", t
->user_agent
);
4534 g_object_set(G_OBJECT(t
->settings
),
4535 "user-agent", t
->user_agent
, (char *)NULL
);
4537 webkit_web_view_set_settings(wv
, t
->settings
);
4541 * This is a little hairy but it comes down to this:
4542 * when we run in whitelist mode we have to assist the browser in
4543 * opening the URL that it would have opened in a new tab.
4545 reason
= webkit_web_navigation_action_get_reason(na
);
4546 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
4547 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4548 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
4549 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4551 webkit_web_policy_decision_use(pd
);
4552 return (TRUE
); /* we made the decision */
4559 webview_rrs_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, WebKitWebResource
*res
,
4560 WebKitNetworkRequest
*request
, WebKitNetworkResponse
*response
,
4566 msg
= webkit_network_request_get_message(request
);
4569 uri
= soup_message_get_uri(msg
);
4572 if (strcmp(soup_uri_get_scheme(uri
), SOUP_URI_SCHEME_HTTP
) == 0) {
4573 if (strict_transport_check(uri
->host
)) {
4574 DNPRINTF(XT_D_NAV
, "webview_rrs_cb: force https for %s\n",
4576 soup_uri_set_scheme(uri
, SOUP_URI_SCHEME_HTTPS
);
4582 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4585 struct domain
*d
= NULL
;
4587 WebKitWebView
*webview
= NULL
;
4590 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
4591 webkit_web_view_get_uri(wv
));
4594 /* open in current tab */
4596 } else if (enable_scripts
== 0 && enable_js_whitelist
== 1) {
4597 uri
= webkit_web_view_get_uri(wv
);
4598 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4601 if (t
->ctrl_click
) {
4602 x
= ctrl_click_focus
;
4605 tt
= create_new_tab(NULL
, NULL
, x
, -1);
4607 } else if (enable_scripts
== 1) {
4608 if (t
->ctrl_click
) {
4609 x
= ctrl_click_focus
;
4612 tt
= create_new_tab(NULL
, NULL
, x
, -1);
4620 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
4623 struct domain
*d
= NULL
;
4625 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
4627 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
4628 uri
= webkit_web_view_get_uri(wv
);
4629 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4633 } else if (enable_scripts
== 1)
4640 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
4642 /* we can not eat the event without throwing gtk off so defer it */
4644 /* catch middle click */
4645 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
4650 /* catch ctrl click */
4651 if (e
->type
== GDK_BUTTON_RELEASE
&&
4652 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
4657 return (XT_CB_PASSTHROUGH
);
4661 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
4663 struct mime_type
*m
;
4665 m
= find_mime_type(mime_type
);
4671 return (fork_exec(t
, m
->mt_action
,
4672 webkit_network_request_get_uri(request
),
4673 "can't launch MIME handler", 0));
4677 get_mime_type(const char *file
)
4680 char *mime_type
= NULL
;
4684 if (g_str_has_prefix(file
, "file://"))
4685 file
+= strlen("file://");
4687 gf
= g_file_new_for_path(file
);
4688 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
4690 if ((m
= g_file_info_get_content_type(fi
)) != NULL
)
4691 mime_type
= g_strdup(m
);
4699 run_download_mimehandler(char *mime_type
, char *file
)
4701 struct mime_type
*m
;
4703 m
= find_mime_type(mime_type
);
4707 return (fork_exec(NULL
, m
->mt_action
, file
,
4708 "can't launch download MIME handler", 0));
4712 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
4715 WebKitDownloadStatus status
;
4716 const char *file
= NULL
;
4719 if (download
== NULL
)
4721 status
= webkit_download_get_status(download
);
4722 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
4725 file
= webkit_download_get_destination_uri(download
);
4728 mime
= get_mime_type(file
);
4732 if (g_str_has_prefix(file
, "file://"))
4733 file
+= strlen("file://");
4734 run_download_mimehandler((char *)mime
, (char *)file
);
4739 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
4740 WebKitNetworkRequest
*request
, char *mime_type
,
4741 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
4744 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
4748 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
4749 t
->tab_id
, mime_type
);
4751 if (run_mimehandler(t
, mime_type
, request
) == 0) {
4752 webkit_web_policy_decision_ignore(decision
);
4757 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
4758 webkit_web_policy_decision_download(decision
);
4766 download_start(struct tab
*t
, struct download
*d
, int flag
)
4768 WebKitNetworkRequest
*req
;
4770 const gchar
*suggested_name
;
4771 gchar
*filename
= NULL
;
4776 if (d
== NULL
|| t
== NULL
) {
4777 show_oops(NULL
, "%s invalid parameters", __func__
);
4781 suggested_name
= webkit_download_get_suggested_filename(d
->download
);
4782 if (suggested_name
== NULL
)
4783 return (FALSE
); /* abort download */
4794 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
4797 uri
= g_strdup_printf("%s\\%s", download_dir
, i
?
4798 filename
: suggested_name
);
4800 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
4801 filename
: suggested_name
);
4805 } while (!stat(uri
, &sb
));
4807 } while (!stat(uri
+ strlen("file://"), &sb
)); /* XXX is the + strlen right? */
4810 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
4811 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
4813 /* if we're restarting the download, or starting
4814 * it after doing something else, we need to recreate
4815 * the download request.
4817 if (flag
== XT_DL_RESTART
) {
4818 req
= webkit_network_request_new(webkit_download_get_uri(d
->download
));
4819 webkit_download_cancel(d
->download
);
4820 g_object_unref(d
->download
);
4821 d
->download
= webkit_download_new(req
);
4824 webkit_download_set_destination_uri(d
->download
, uri
);
4826 if (webkit_download_get_status(d
->download
) ==
4827 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
4828 show_oops(t
, "%s: download failed to start", __func__
);
4830 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
4832 /* connect "download first" mime handler */
4833 g_signal_connect(G_OBJECT(d
->download
), "notify::status",
4834 G_CALLBACK(download_status_changed_cb
), NULL
);
4836 /* get from history */
4837 g_object_ref(d
->download
);
4838 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
4839 show_oops(t
, "Download of '%s' started...",
4840 basename((char *)webkit_download_get_destination_uri(d
->download
)));
4843 if (flag
!= XT_DL_START
)
4844 webkit_download_start(d
->download
);
4846 DNPRINTF(XT_D_DOWNLOAD
, "download status : %d",
4847 webkit_download_get_status(d
->download
));
4849 /* sync other download manager tabs */
4850 update_download_tabs(NULL
);
4862 download_ask_cb(struct tab
*t
, GdkEventKey
*e
, gpointer data
)
4864 struct download
*d
= data
;
4868 t
->mode_cb_data
= NULL
;
4871 e
->keyval
= GDK_Escape
;
4872 return (XT_CB_PASSTHROUGH
);
4875 DPRINTF("download_ask_cb: User pressed %c\n", e
->keyval
);
4876 if (e
->keyval
== 'y' || e
->keyval
== 'Y' || e
->keyval
== GDK_Return
)
4877 /* We need to do a RESTART, because we're not calling from
4878 * webview_download_cb
4880 download_start(t
, d
, XT_DL_RESTART
);
4882 /* for all other keyvals, we just let the download be */
4883 e
->keyval
= GDK_Escape
;
4884 return (XT_CB_HANDLED
);
4888 download_ask(struct tab
*t
, struct download
*d
)
4890 const gchar
*suggested_name
;
4892 suggested_name
= webkit_download_get_suggested_filename(d
->download
);
4893 if (suggested_name
== NULL
)
4894 return (FALSE
); /* abort download */
4896 show_oops(t
, "download file %s [y/n] ?", suggested_name
);
4897 t
->mode_cb
= download_ask_cb
;
4898 t
->mode_cb_data
= d
;
4904 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
4907 const gchar
*suggested_name
;
4908 struct download
*download_entry
;
4911 if (wk_download
== NULL
|| t
== NULL
) {
4912 show_oops(NULL
, "%s invalid parameters", __func__
);
4916 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
4917 if (suggested_name
== NULL
)
4918 return (FALSE
); /* abort download */
4920 download_entry
= g_malloc(sizeof(struct download
));
4921 download_entry
->download
= wk_download
;
4922 download_entry
->tab
= t
;
4923 download_entry
->id
= next_download_id
++;
4924 RB_INSERT(download_list
, &downloads
, download_entry
);
4926 if (download_mode
== XT_DM_START
)
4927 ret
= download_start(t
, download_entry
, XT_DL_START
);
4928 else if (download_mode
== XT_DM_ASK
)
4929 ret
= download_ask(t
, download_entry
);
4930 else if (download_mode
== XT_DM_ADD
)
4931 show_oops(t
, "added %s to download manager",
4934 /* sync other download manager tabs */
4935 update_download_tabs(NULL
);
4938 * NOTE: never redirect/render the current tab before this
4939 * function returns. This will cause the download to never start.
4941 return (ret
); /* start download */
4945 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
4947 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
4950 show_oops(NULL
, "webview_hover_cb");
4955 set_status(t
, uri
, XT_STATUS_LINK
);
4958 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
4963 mark(struct tab
*t
, struct karg
*arg
)
4970 if ((index
= marktoindex(mark
)) == -1)
4973 if (arg
->i
== XT_MARK_SET
)
4974 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
4975 else if (arg
->i
== XT_MARK_GOTO
) {
4976 if (t
->mark
[index
] == XT_INVALID_MARK
) {
4977 show_oops(t
, "mark '%c' does not exist", mark
);
4980 /* XXX t->mark[index] can be bigger than the maximum if ajax or
4981 something changes the document size */
4982 pos
= gtk_adjustment_get_value(t
->adjust_v
);
4983 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
4984 t
->mark
[marktoindex('\'')] = pos
;
4991 marks_clear(struct tab
*t
)
4995 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
4996 t
->mark
[i
] = XT_INVALID_MARK
;
5002 char file
[PATH_MAX
];
5003 char *line
= NULL
, *p
;
5008 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_QMARKS_FILE
);
5009 if ((f
= fopen(file
, "r+")) == NULL
) {
5010 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
5014 for (i
= 1; ; i
++) {
5015 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
5017 if (strlen(line
) == 0 || line
[0] == '#') {
5023 p
= strtok(line
, " \t");
5025 if (p
== NULL
|| strlen(p
) != 1 ||
5026 (index
= qmarktoindex(*p
)) == -1) {
5027 warnx("corrupt quickmarks file, line %d", i
);
5031 p
= strtok(NULL
, " \t");
5032 if (qmarks
[index
] != NULL
)
5033 g_free(qmarks
[index
]);
5034 qmarks
[index
] = g_strdup(p
);
5045 char file
[PATH_MAX
];
5049 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_QMARKS_FILE
);
5050 if ((f
= fopen(file
, "r+")) == NULL
) {
5051 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
5055 for (i
= 0; i
< XT_NOQMARKS
; i
++)
5056 if (qmarks
[i
] != NULL
)
5057 fprintf(f
, "%c %s\n", indextoqmark(i
), qmarks
[i
]);
5065 qmark(struct tab
*t
, struct karg
*arg
)
5070 mark
= arg
->s
[strlen(arg
->s
)-1];
5071 index
= qmarktoindex(mark
);
5077 if (qmarks
[index
] != NULL
) {
5078 g_free(qmarks
[index
]);
5079 qmarks
[index
] = NULL
;
5082 qmarks_load(); /* sync if multiple instances */
5083 qmarks
[index
] = g_strdup(get_uri(t
));
5087 if (qmarks
[index
] != NULL
)
5088 load_uri(t
, qmarks
[index
]);
5090 show_oops(t
, "quickmark \"%c\" does not exist",
5096 if (qmarks
[index
] != NULL
)
5097 create_new_tab(qmarks
[index
], NULL
, 1, -1);
5099 show_oops(t
, "quickmark \"%c\" does not exist",
5110 go_up(struct tab
*t
, struct karg
*args
)
5118 if (args
->i
== XT_GO_UP_ROOT
)
5119 levels
= XT_GO_UP_ROOT
;
5120 else if ((levels
= atoi(args
->s
)) == 0)
5123 uri
= g_strdup(get_uri(t
));
5127 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
5130 tmp
+= strlen(XT_PROTO_DELIM
);
5132 /* it makes no sense to strip the last slash from ".../dir/", skip it */
5133 lastidx
= strlen(tmp
) - 1;
5135 if (tmp
[lastidx
] == '/')
5136 tmp
[lastidx
] = '\0';
5140 p
= strrchr(tmp
, '/');
5141 if (p
== tmp
) { /* Are we at the root of a file://-path? */
5144 } else if (p
!= NULL
)
5157 gototab(struct tab
*t
, struct karg
*args
)
5160 struct karg arg
= {0, NULL
, -1};
5162 tab
= atoi(args
->s
);
5165 arg
.i
= XT_TAB_NEXT
;
5177 zoom_amount(struct tab
*t
, struct karg
*arg
)
5179 struct karg narg
= {0, NULL
, -1};
5181 narg
.i
= atoi(arg
->s
);
5182 resizetab(t
, &narg
);
5188 flip_colon(struct tab
*t
, struct karg
*arg
)
5190 struct karg narg
= {0, NULL
, -1};
5193 if (t
== NULL
|| arg
== NULL
)
5196 p
= strstr(arg
->s
, ":");
5208 /* buffer commands receive the regex that triggered them in arg.s */
5209 char bcmd
[XT_BUFCMD_SZ
];
5213 #define XT_PRE_NO (0)
5214 #define XT_PRE_YES (1)
5215 #define XT_PRE_MAYBE (2)
5217 int (*func
)(struct tab
*, struct karg
*);
5221 { "^[0-9]*gu$", XT_PRE_MAYBE
, "gu", go_up
, 0 },
5222 { "^gU$", XT_PRE_NO
, "gU", go_up
, XT_GO_UP_ROOT
},
5223 { "^gg$", XT_PRE_NO
, "gg", move
, XT_MOVE_TOP
},
5224 { "^gG$", XT_PRE_NO
, "gG", move
, XT_MOVE_BOTTOM
},
5225 { "^[0-9]+%$", XT_PRE_YES
, "%", move
, XT_MOVE_PERCENT
},
5226 { "^zz$", XT_PRE_NO
, "zz", move
, XT_MOVE_CENTER
},
5227 { "^gh$", XT_PRE_NO
, "gh", go_home
, 0 },
5228 { "^m[a-zA-Z0-9]$", XT_PRE_NO
, "m", mark
, XT_MARK_SET
},
5229 { "^['][a-zA-Z0-9']$", XT_PRE_NO
, "'", mark
, XT_MARK_GOTO
},
5230 { "^[0-9]+t$", XT_PRE_YES
, "t", gototab
, 0 },
5231 { "^g0$", XT_PRE_YES
, "g0", movetab
, XT_TAB_FIRST
},
5232 { "^g[$]$", XT_PRE_YES
, "g$", movetab
, XT_TAB_LAST
},
5233 { "^[0-9]*gt$", XT_PRE_YES
, "t", movetab
, XT_TAB_NEXT
},
5234 { "^[0-9]*gT$", XT_PRE_YES
, "T", movetab
, XT_TAB_PREV
},
5235 { "^M[a-zA-Z0-9]$", XT_PRE_NO
, "M", qmark
, XT_QMARK_SET
},
5236 { "^go[a-zA-Z0-9]$", XT_PRE_NO
, "go", qmark
, XT_QMARK_OPEN
},
5237 { "^gn[a-zA-Z0-9]$", XT_PRE_NO
, "gn", qmark
, XT_QMARK_TAB
},
5238 { "^ZR$", XT_PRE_NO
, "ZR", restart
, 0 },
5239 { "^ZZ$", XT_PRE_NO
, "ZZ", quit
, 0 },
5240 { "^zi$", XT_PRE_NO
, "zi", resizetab
, XT_ZOOM_IN
},
5241 { "^zo$", XT_PRE_NO
, "zo", resizetab
, XT_ZOOM_OUT
},
5242 { "^z0$", XT_PRE_NO
, "z0", resizetab
, XT_ZOOM_NORMAL
},
5243 { "^[0-9]+Z$", XT_PRE_YES
, "Z", zoom_amount
, 0 },
5244 { "^[0-9]+:$", XT_PRE_YES
, ":", flip_colon
, 0 },
5248 buffercmd_init(void)
5252 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
5253 if (regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
5254 REG_EXTENDED
| REG_NOSUB
))
5255 startpage_add("invalid buffercmd regex %s",
5256 buffercmds
[i
].regex
);
5260 buffercmd_abort(struct tab
*t
)
5267 DNPRINTF(XT_D_BUFFERCMD
, "%s: clearing buffer\n", __func__
);
5269 for (i
= 0; i
< LENGTH(bcmd
); i
++)
5272 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
5273 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
5277 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
5279 struct karg arg
= {0, NULL
, -1};
5282 arg
.s
= g_strdup(bcmd
);
5284 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
5285 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
5295 buffercmd_addkey(struct tab
*t
, guint keyval
)
5298 char s
[XT_BUFCMD_SZ
];
5300 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
))) {
5302 return (XT_CB_PASSTHROUGH
);
5305 if (keyval
== GDK_Escape
) {
5307 return (XT_CB_HANDLED
);
5310 /* key with modifier or non-ascii character */
5311 if (!isascii(keyval
)) {
5313 * XXX this looks wrong but fixes some sites like
5314 * http://www.seslisozluk.com/
5315 * that eat a shift or ctrl and end putting default focus in js
5316 * instead of ignoring the keystroke
5317 * so instead of return (XT_CB_PASSTHROUGH); eat the key
5319 return (XT_CB_HANDLED
);
5322 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
5323 "to buffer \"%s\"\n", keyval
, bcmd
);
5325 for (i
= 0; i
< LENGTH(bcmd
); i
++)
5326 if (bcmd
[i
] == '\0') {
5331 /* buffer full, ignore input */
5332 if (i
>= LENGTH(bcmd
) -1) {
5333 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
5335 return (XT_CB_HANDLED
);
5338 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
5340 /* find exact match */
5341 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
5342 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
5343 (size_t) 0, NULL
, 0) == 0) {
5344 buffercmd_execute(t
, &buffercmds
[i
]);
5348 /* find non exact matches to see if we need to abort ot not */
5349 for (i
= 0, match
= 0; i
< LENGTH(buffercmds
); i
++) {
5350 DNPRINTF(XT_D_BUFFERCMD
, "trying: %s\n", bcmd
);
5353 if (buffercmds
[i
].precount
== XT_PRE_MAYBE
) {
5354 if (isdigit(bcmd
[0])) {
5355 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
5359 if (sscanf(bcmd
, "%s", s
) == 0)
5362 } else if (buffercmds
[i
].precount
== XT_PRE_YES
) {
5363 if (sscanf(bcmd
, "%d%s", &c
, s
) == 0)
5366 if (sscanf(bcmd
, "%s", s
) == 0)
5369 if (c
== -1 && buffercmds
[i
].precount
)
5371 if (!strncmp(s
, buffercmds
[i
].cmd
, strlen(s
)))
5374 DNPRINTF(XT_D_BUFFERCMD
, "got[%d] %d <%s>: %d %s\n",
5375 i
, match
, buffercmds
[i
].cmd
, c
, s
);
5378 DNPRINTF(XT_D_BUFFERCMD
, "aborting: %s\n", bcmd
);
5383 return (XT_CB_HANDLED
);
5387 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
5389 struct key_binding
*k
;
5391 /* handle keybindings if buffercmd is empty.
5392 if not empty, allow commands like C-n */
5393 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
5394 TAILQ_FOREACH(k
, &kbl
, entry
)
5395 if (e
->keyval
== k
->key
5396 && (entry
? k
->use_in_entry
: 1)) {
5397 /* when we are edditing eat ctrl/mod keys */
5398 if (edit_mode
== XT_EM_VI
&&
5399 t
->mode
== XT_MODE_INSERT
&&
5400 (e
->state
& CTRL
|| e
->state
& MOD1
))
5401 return (XT_CB_PASSTHROUGH
);
5404 if ((e
->state
& (CTRL
| MOD1
)) == 0)
5405 return (cmd_execute(t
, k
->cmd
));
5406 } else if ((e
->state
& k
->mask
) == k
->mask
) {
5407 return (cmd_execute(t
, k
->cmd
));
5411 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
5412 return buffercmd_addkey(t
, e
->keyval
);
5414 return (XT_CB_PASSTHROUGH
);
5418 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5422 /* don't use w directly; use t->whatever instead */
5425 show_oops(NULL
, "wv_keypress_cb");
5426 return (XT_CB_PASSTHROUGH
);
5432 return t
->mode_cb(t
, e
, t
->mode_cb_data
);
5434 DNPRINTF(XT_D_KEY
, "wv_keypress_cb: mode %d keyval 0x%x mask "
5435 "0x%x tab %d\n", t
->mode
, e
->keyval
, e
->state
, t
->tab_id
);
5437 /* Hide buffers, if they are visible, with escape. */
5438 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
5439 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
5440 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5442 return (XT_CB_HANDLED
);
5445 if (t
->mode
== XT_MODE_HINT
)
5446 return (XT_CB_HANDLED
);
5448 if ((CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Tab
) ||
5449 (CLEAN(e
->state
) == SHFT
&& e
->keyval
== GDK_Tab
))
5450 /* something focussy is about to happen */
5451 return (XT_CB_PASSTHROUGH
);
5453 /* check if we are some sort of text input thing in the dom */
5454 input_check_mode(t
);
5456 if (t
->mode
== XT_MODE_HINT
) {
5457 /* XXX make sure cmd entry is enabled */
5458 return (XT_CB_HANDLED
);
5459 } else if (t
->mode
== XT_MODE_PASSTHROUGH
) {
5460 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
)
5461 t
->mode
= XT_MODE_COMMAND
;
5462 return (XT_CB_PASSTHROUGH
);
5463 } else if (t
->mode
== XT_MODE_COMMAND
) {
5465 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5466 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
5467 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
5468 return (handle_keypress(t
, e
, 0));
5471 return (handle_keypress(t
, e
, 1));
5475 return (XT_CB_PASSTHROUGH
);
5479 hint_continue(struct tab
*t
)
5481 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
5483 const gchar
*errstr
= NULL
;
5487 if (!(c
[0] == '.' || c
[0] == ','))
5489 if (strlen(c
) == 1) {
5490 /* XXX should not happen */
5495 if (isdigit(c
[1])) {
5497 i
= strtonum(&c
[1], 1, 4096, &errstr
);
5499 show_oops(t
, "invalid numerical hint %s", &c
[1]);
5502 s
= g_strdup_printf("hints.updateHints(%d);", i
);
5506 /* alphanumeric input */
5507 s
= g_strdup_printf("hints.createHints('%s', '%c');",
5508 &c
[1], c
[0] == '.' ? 'f' : 'F');
5519 search_continue(struct tab
*t
)
5521 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
5522 gboolean rv
= FALSE
;
5524 if (c
[0] == ':' || c
[0] == '.' || c
[0] == ',')
5526 if (strlen(c
) == 1) {
5527 webkit_web_view_unmark_text_matches(t
->wv
);
5532 t
->search_forward
= TRUE
;
5533 else if (c
[0] == '?')
5534 t
->search_forward
= FALSE
;
5544 search_cb(struct tab
*t
)
5546 const gchar
*c
= gtk_entry_get_text(GTK_ENTRY(t
->cmd
));
5549 if (search_continue(t
) == FALSE
)
5553 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, t
->search_forward
,
5555 /* not found, mark red */
5556 gdk_color_parse(XT_COLOR_RED
, &color
);
5557 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5558 /* unmark and remove selection */
5559 webkit_web_view_unmark_text_matches(t
->wv
);
5560 /* my kingdom for a way to unselect text in webview */
5562 /* found, highlight all */
5563 webkit_web_view_unmark_text_matches(t
->wv
);
5564 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
5565 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5566 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
,
5567 &t
->default_style
->base
[GTK_STATE_NORMAL
]);
5575 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5577 const gchar
*c
= gtk_entry_get_text(w
);
5580 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
5581 return (XT_CB_PASSTHROUGH
);
5584 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x tab %d\n",
5585 e
->keyval
, e
->state
, t
->tab_id
);
5588 if (!(e
->keyval
== GDK_Tab
|| e
->keyval
== GDK_ISO_Left_Tab
)) {
5589 if (hint_continue(t
) == FALSE
)
5594 if (search_continue(t
) == FALSE
)
5597 /* if search length is > 4 then no longer play timeout games */
5598 if (strlen(c
) > 4) {
5600 g_source_remove(t
->search_id
);
5607 /* reestablish a new timer if the user types fast */
5609 g_source_remove(t
->search_id
);
5610 t
->search_id
= g_timeout_add(250, (GSourceFunc
)search_cb
, (gpointer
)t
);
5613 return (XT_CB_PASSTHROUGH
);
5617 match_uri(const gchar
*uri
, const gchar
*key
) {
5620 gboolean match
= FALSE
;
5624 if (!strncmp(key
, uri
, len
))
5627 voffset
= strstr(uri
, "/") + 2;
5628 if (!strncmp(key
, voffset
, len
))
5630 else if (g_str_has_prefix(voffset
, "www.")) {
5631 voffset
= voffset
+ strlen("www.");
5632 if (!strncmp(key
, voffset
, len
))
5641 match_session(const gchar
*name
, const gchar
*key
) {
5644 sub
= strcasestr(name
, key
);
5650 cmd_getlist(int id
, char *key
)
5657 if (cmds
[id
].type
& XT_URLARG
) {
5658 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
5659 if (match_uri(h
->uri
, key
)) {
5660 cmd_status
.list
[c
] = (char *)h
->uri
;
5666 } else if (cmds
[id
].type
& XT_SESSARG
) {
5667 TAILQ_FOREACH(s
, &sessions
, entry
)
5668 if (match_session(s
->name
, key
)) {
5669 cmd_status
.list
[c
] = (char *)s
->name
;
5675 } else if (cmds
[id
].type
& XT_SETARG
) {
5676 for (i
= 0; i
< get_settings_size(); i
++)
5677 if (!strncmp(key
, get_setting_name(i
),
5679 cmd_status
.list
[c
++] =
5680 get_setting_name(i
);
5686 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
5688 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
5689 if (cmds
[i
].level
< dep
)
5691 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
5692 strlen(key
)) && !isdigit(cmds
[i
].cmd
[0]))
5693 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
5701 cmd_getnext(int dir
)
5703 cmd_status
.index
+= dir
;
5705 if (cmd_status
.index
< 0)
5706 cmd_status
.index
= cmd_status
.len
- 1;
5707 else if (cmd_status
.index
>= cmd_status
.len
)
5708 cmd_status
.index
= 0;
5710 return cmd_status
.list
[cmd_status
.index
];
5714 cmd_tokenize(char *s
, char *tokens
[])
5717 char *tok
, *last
= NULL
;
5718 size_t len
= strlen(s
);
5721 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
5722 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
5723 tok
= strtok_r(NULL
, " ", &last
), i
++)
5733 cmd_complete(struct tab
*t
, char *str
, int dir
)
5735 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
5736 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
5738 char *tok
, *match
, *s
= g_strdup(str
);
5740 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
5743 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
5746 for (i
= 0; isdigit(s
[i
]); i
++)
5749 for (; isspace(s
[i
]); i
++)
5754 levels
= cmd_tokenize(s
, tokens
);
5756 for (i
= 0; i
< levels
- 1; i
++) {
5759 for (j
= c
; j
< LENGTH(cmds
); j
++) {
5760 if (cmds
[j
].level
< dep
)
5762 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
5766 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
5773 if (matchcount
== 1) {
5774 strlcat(res
, tok
, sizeof res
);
5775 strlcat(res
, " ", sizeof res
);
5785 if (cmd_status
.index
== -1)
5786 cmd_getlist(parent
, tokens
[i
]);
5788 if (cmd_status
.len
> 0) {
5789 match
= cmd_getnext(dir
);
5790 strlcat(res
, match
, sizeof res
);
5791 gtk_entry_set_text(w
, res
);
5792 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
5799 cmd_execute(struct tab
*t
, char *str
)
5801 struct cmd
*cmd
= NULL
;
5802 char *tok
, *last
= NULL
, *s
= g_strdup(str
), *sc
;
5804 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
5805 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
5806 struct karg arg
= {0, NULL
, -1};
5811 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
5817 while (isspace(s
[0]))
5820 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
5821 prefix
= atoi(prefixstr
);
5825 for (tok
= strtok_r(s
, " ", &last
); tok
;
5826 tok
= strtok_r(NULL
, " ", &last
)) {
5828 for (j
= c
; j
< LENGTH(cmds
); j
++) {
5829 if (cmds
[j
].level
< dep
)
5831 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
5833 if (cmds
[j
].level
== dep
&&
5834 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
5838 if (len
== strlen(cmds
[j
].cmd
)) {
5844 if (matchcount
== 1) {
5849 show_oops(t
, "Invalid command: %s", str
);
5855 show_oops(t
, "Empty command");
5861 arg
.precount
= prefix
;
5862 else if (cmd_prefix
> 0)
5863 arg
.precount
= cmd_prefix
;
5865 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.precount
> -1) {
5866 show_oops(t
, "No prefix allowed: %s", str
);
5870 arg
.s
= last
? g_strdup(last
) : g_strdup("");
5871 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
5872 if (arg
.s
== NULL
) {
5873 show_oops(t
, "Invalid command");
5876 arg
.precount
= atoi(arg
.s
);
5877 if (arg
.precount
<= 0) {
5878 if (arg
.s
[0] == '0')
5879 show_oops(t
, "Zero count");
5881 show_oops(t
, "Trailing characters");
5886 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n",
5887 __func__
, arg
.precount
, arg
.s
);
5903 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5906 show_oops(NULL
, "entry_key_cb invalid parameters");
5907 return (XT_CB_PASSTHROUGH
);
5910 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x tab %d\n",
5911 e
->keyval
, e
->state
, t
->tab_id
);
5915 if (e
->keyval
== GDK_Escape
) {
5916 /* don't use focus_webview(t) because we want to type :cmds */
5917 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5920 return (handle_keypress(t
, e
, 1));
5923 struct command_entry
*
5924 history_prev(struct command_list
*l
, struct command_entry
*at
)
5927 at
= TAILQ_LAST(l
, command_list
);
5929 at
= TAILQ_PREV(at
, command_list
, entry
);
5931 at
= TAILQ_LAST(l
, command_list
);
5937 struct command_entry
*
5938 history_next(struct command_list
*l
, struct command_entry
*at
)
5941 at
= TAILQ_FIRST(l
);
5943 at
= TAILQ_NEXT(at
, entry
);
5945 at
= TAILQ_FIRST(l
);
5952 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5954 int rv
= XT_CB_HANDLED
;
5955 const gchar
*c
= gtk_entry_get_text(w
);
5959 show_oops(NULL
, "cmd_keypress_cb parameters");
5960 return (XT_CB_PASSTHROUGH
);
5963 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x tab %d\n",
5964 e
->keyval
, e
->state
, t
->tab_id
);
5968 e
->keyval
= GDK_Escape
;
5969 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?' ||
5970 c
[0] == '.' || c
[0] == ','))
5971 e
->keyval
= GDK_Escape
;
5973 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
5974 e
->keyval
!= GDK_ISO_Left_Tab
)
5975 cmd_status
.index
= -1;
5977 switch (e
->keyval
) {
5980 cmd_complete(t
, (char *)&c
[1], 1);
5981 else if (c
[0] == '.' || c
[0] == ',')
5982 run_script(t
, "hints.focusNextHint();");
5984 case GDK_ISO_Left_Tab
:
5986 cmd_complete(t
, (char *)&c
[1], -1);
5987 else if (c
[0] == '.' || c
[0] == ',')
5988 run_script(t
, "hints.focusPreviousHint();");
5992 if ((search_at
= history_next(&shl
, search_at
))) {
5993 search_at
->line
[0] = c
[0];
5994 gtk_entry_set_text(w
, search_at
->line
);
5995 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
5997 } else if (c
[0] == '/') {
5998 if ((search_at
= history_prev(&shl
, search_at
))) {
5999 search_at
->line
[0] = c
[0];
6000 gtk_entry_set_text(w
, search_at
->line
);
6001 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6003 } if (c
[0] == ':') {
6004 if ((history_at
= history_prev(&chl
, history_at
))) {
6005 history_at
->line
[0] = c
[0];
6006 gtk_entry_set_text(w
, history_at
->line
);
6007 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6013 if ((search_at
= history_next(&shl
, search_at
))) {
6014 search_at
->line
[0] = c
[0];
6015 gtk_entry_set_text(w
, search_at
->line
);
6016 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6018 } else if (c
[0] == '?') {
6019 if ((search_at
= history_prev(&shl
, search_at
))) {
6020 search_at
->line
[0] = c
[0];
6021 gtk_entry_set_text(w
, search_at
->line
);
6022 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6024 } if (c
[0] == ':') {
6025 if ((history_at
= history_next(&chl
, history_at
))) {
6026 history_at
->line
[0] = c
[0];
6027 gtk_entry_set_text(w
, history_at
->line
);
6028 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6033 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?") ||
6034 !strcmp(c
, ".") || !strcmp(c
, ","))) {
6035 /* see if we are doing hinting and reset it */
6036 if (c
[0] == '.' || c
[0] == ',') {
6037 /* recreate hints */
6038 s
= g_strdup_printf("hints.createHints('', "
6039 "'%c');", c
[0] == '.' ? 'f' : 'F');
6052 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
6053 webkit_web_view_unmark_text_matches(t
->wv
);
6055 /* no need to cancel hints */
6059 rv
= XT_CB_PASSTHROUGH
;
6065 wv_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
6067 DNPRINTF(XT_D_CMD
, "wv_popup_cb: tab %d\n", t
->tab_id
);
6071 cmd_popup_cb(GtkEntry
*entry
, GtkMenu
*menu
, struct tab
*t
)
6073 /* popup menu enabled */
6078 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6081 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
6082 return (XT_CB_PASSTHROUGH
);
6085 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d popup %d\n",
6086 t
->tab_id
, t
->popup
);
6088 /* if popup is enabled don't lose focus */
6091 return (XT_CB_PASSTHROUGH
);
6098 if (show_url
== 0 || t
->focus_wv
)
6101 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6103 return (XT_CB_PASSTHROUGH
);
6107 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6110 const gchar
*c
= gtk_entry_get_text(entry
);
6113 show_oops(NULL
, "cmd_activate_cb invalid parameters");
6117 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6122 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?' ||
6123 c
[0] == '.' || c
[0] == ','))
6129 if (c
[0] == '/' || c
[0] == '?') {
6130 /* see if there is a timer pending */
6132 g_source_remove(t
->search_id
);
6137 if (t
->search_text
) {
6138 g_free(t
->search_text
);
6139 t
->search_text
= NULL
;
6142 t
->search_text
= g_strdup(s
);
6144 g_free(global_search
);
6145 global_search
= g_strdup(s
);
6146 t
->search_forward
= c
[0] == '/';
6148 history_add(&shl
, search_file
, s
, &search_history_count
);
6149 } else if (c
[0] == '.' || c
[0] == ',') {
6150 run_script(t
, "hints.fire();");
6151 /* XXX history for link following? */
6152 } else if (c
[0] == ':') {
6153 history_add(&chl
, command_file
, s
, &cmd_history_count
);
6154 /* can't call hide_cmd after cmd_execute */
6165 backward_cb(GtkWidget
*w
, struct tab
*t
)
6170 show_oops(NULL
, "backward_cb invalid parameters");
6174 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6181 forward_cb(GtkWidget
*w
, struct tab
*t
)
6186 show_oops(NULL
, "forward_cb invalid parameters");
6190 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6192 a
.i
= XT_NAV_FORWARD
;
6197 home_cb(GtkWidget
*w
, struct tab
*t
)
6200 show_oops(NULL
, "home_cb invalid parameters");
6204 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
6210 stop_cb(GtkWidget
*w
, struct tab
*t
)
6212 WebKitWebFrame
*frame
;
6215 show_oops(NULL
, "stop_cb invalid parameters");
6219 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6221 frame
= webkit_web_view_get_main_frame(t
->wv
);
6222 if (frame
== NULL
) {
6223 show_oops(t
, "stop_cb: no frame");
6227 webkit_web_frame_stop_loading(frame
);
6228 abort_favicon_download(t
);
6232 setup_webkit(struct tab
*t
)
6234 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
6235 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
6236 FALSE
, (char *)NULL
);
6238 warnx("webkit does not have \"enable-dns-prefetching\" property");
6239 g_object_set(G_OBJECT(t
->settings
),
6240 "user-agent", t
->user_agent
, (char *)NULL
);
6241 g_object_set(G_OBJECT(t
->settings
),
6242 "enable-scripts", enable_scripts
, (char *)NULL
);
6243 g_object_set(G_OBJECT(t
->settings
),
6244 "enable-plugins", enable_plugins
, (char *)NULL
);
6245 g_object_set(G_OBJECT(t
->settings
),
6246 "javascript-can-open-windows-automatically", enable_scripts
,
6248 g_object_set(G_OBJECT(t
->settings
),
6249 "enable-html5-database", FALSE
, (char *)NULL
);
6250 g_object_set(G_OBJECT(t
->settings
),
6251 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
6252 g_object_set(G_OBJECT(t
->settings
),
6253 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
6254 g_object_set(G_OBJECT(t
->settings
),
6255 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
6256 g_object_set(G_OBJECT(t
->settings
),
6257 "enable-developer-extras", TRUE
, (char *)NULL
);
6258 g_object_set(G_OBJECT(t
->wv
),
6259 "full-content-zoom", TRUE
, (char *)NULL
);
6260 g_object_set(G_OBJECT(t
->settings
),
6261 "auto-load-images", auto_load_images
, (char *)NULL
);
6263 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6267 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
6269 struct tab
*ti
, *t
= NULL
;
6270 gdouble view_size
, value
, max
;
6273 TAILQ_FOREACH(ti
, &tabs
, entry
)
6274 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
6282 if (adjustment
== NULL
)
6283 adjustment
= gtk_scrolled_window_get_vadjustment(
6284 GTK_SCROLLED_WINDOW(t
->browser_win
));
6286 view_size
= gtk_adjustment_get_page_size(adjustment
);
6287 value
= gtk_adjustment_get_value(adjustment
);
6288 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
6291 position
= g_strdup("All");
6292 else if (value
== max
)
6293 position
= g_strdup("Bot");
6294 else if (value
== 0)
6295 position
= g_strdup("Top");
6297 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
6299 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
6306 create_window(const gchar
*name
)
6310 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6311 if (window_maximize
)
6312 gtk_window_maximize(GTK_WINDOW(w
));
6314 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6315 gtk_widget_set_name(w
, name
);
6316 gtk_window_set_wmclass(GTK_WINDOW(w
), name
, "XXXTerm");
6322 create_browser(struct tab
*t
)
6326 GtkAdjustment
*adjustment
;
6329 show_oops(NULL
, "create_browser invalid parameters");
6333 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
6334 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
6335 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
6336 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
6338 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
6339 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
6340 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
6342 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
6343 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
6346 t
->settings
= webkit_web_settings_new();
6348 g_object_set(t
->settings
, "default-encoding", encoding
, (char *)NULL
);
6350 if (user_agent
== NULL
) {
6351 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
6353 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
6356 t
->user_agent
= g_strdup(user_agent
->value
);
6358 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
6359 t
->load_images
= auto_load_images
;
6362 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
6363 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
6364 G_CALLBACK(update_statusbar_position
), NULL
);
6373 create_kiosk_toolbar(struct tab
*t
)
6375 GtkWidget
*toolbar
= NULL
, *b
;
6377 b
= gtk_hbox_new(FALSE
, 0);
6379 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6381 /* backward button */
6382 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
6383 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6384 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6385 G_CALLBACK(backward_cb
), t
);
6386 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
6388 /* forward button */
6389 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
6390 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6391 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6392 G_CALLBACK(forward_cb
), t
);
6393 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
6396 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
6397 gtk_widget_set_sensitive(t
->gohome
, true);
6398 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
6399 G_CALLBACK(home_cb
), t
);
6400 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
6402 /* create widgets but don't use them */
6403 t
->uri_entry
= gtk_entry_new();
6404 t
->default_style
= gtk_rc_get_style(t
->uri_entry
);
6405 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6406 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6407 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6413 create_toolbar(struct tab
*t
)
6415 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
6417 b
= gtk_hbox_new(FALSE
, 0);
6419 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6421 /* backward button */
6422 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
6423 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6424 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6425 G_CALLBACK(backward_cb
), t
);
6426 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
6428 /* forward button */
6429 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
6430 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6431 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6432 G_CALLBACK(forward_cb
), t
);
6433 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
6437 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6438 gtk_widget_set_sensitive(t
->stop
, FALSE
);
6439 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
6440 G_CALLBACK(stop_cb
), t
);
6441 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
6445 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6446 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6447 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
6448 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
6449 G_CALLBACK(js_toggle_cb
), t
);
6450 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
6452 t
->uri_entry
= gtk_entry_new();
6453 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
6454 G_CALLBACK(activate_uri_entry_cb
), t
);
6455 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
6456 G_CALLBACK(entry_key_cb
), t
);
6458 eb1
= gtk_hbox_new(FALSE
, 0);
6459 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
6460 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
6461 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
6464 if (search_string
) {
6466 t
->search_entry
= gtk_entry_new();
6467 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
6468 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
6469 G_CALLBACK(activate_search_entry_cb
), t
);
6470 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
6471 G_CALLBACK(entry_key_cb
), t
);
6472 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
6473 eb2
= gtk_hbox_new(FALSE
, 0);
6474 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
6475 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
6477 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
6479 t
->default_style
= gtk_rc_get_style(t
->uri_entry
);
6485 create_buffers(struct tab
*t
)
6487 GtkCellRenderer
*renderer
;
6490 view
= gtk_tree_view_new();
6492 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
6494 renderer
= gtk_cell_renderer_text_new();
6495 gtk_tree_view_insert_column_with_attributes
6496 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, (char *)NULL
);
6498 renderer
= gtk_cell_renderer_pixbuf_new();
6499 gtk_tree_view_insert_column_with_attributes
6500 (GTK_TREE_VIEW(view
), -1, "Favicon", renderer
, "pixbuf", COL_FAVICON
,
6503 renderer
= gtk_cell_renderer_text_new();
6504 gtk_tree_view_insert_column_with_attributes
6505 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
6508 gtk_tree_view_set_model
6509 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
6515 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
6516 GtkTreeViewColumn
*col
, struct tab
*t
)
6521 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6523 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
6526 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
6527 set_current_tab(id
- 1);
6533 /* after tab reordering/creation/removal */
6540 TAILQ_FOREACH(t
, &tabs
, entry
) {
6541 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
6542 if (t
->tab_id
> maxid
)
6545 gtk_widget_show(t
->tab_elems
.sep
);
6548 TAILQ_FOREACH(t
, &tabs
, entry
) {
6549 if (t
->tab_id
== maxid
) {
6550 gtk_widget_hide(t
->tab_elems
.sep
);
6556 /* after active tab change */
6558 recolor_compact_tabs(void)
6564 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
6565 TAILQ_FOREACH(t
, &tabs
, entry
)
6566 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
6569 curid
= gtk_notebook_get_current_page(notebook
);
6570 TAILQ_FOREACH(t
, &tabs
, entry
)
6571 if (t
->tab_id
== curid
) {
6572 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
6573 gtk_widget_modify_fg(t
->tab_elems
.label
,
6574 GTK_STATE_NORMAL
, &color
);
6580 set_current_tab(int page_num
)
6582 buffercmd_abort(get_current_tab());
6583 gtk_notebook_set_current_page(notebook
, page_num
);
6584 recolor_compact_tabs();
6588 undo_close_tab_save(struct tab
*t
)
6592 struct undo
*u1
, *u2
;
6594 WebKitWebHistoryItem
*item
;
6596 if ((uri
= get_uri(t
)) == NULL
)
6599 u1
= g_malloc0(sizeof(struct undo
));
6600 u1
->uri
= g_strdup(uri
);
6602 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6604 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
6605 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
6608 /* forward history */
6609 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
6613 u1
->history
= g_list_prepend(u1
->history
,
6614 webkit_web_history_item_copy(item
));
6615 items
= g_list_next(items
);
6620 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
6621 u1
->history
= g_list_prepend(u1
->history
,
6622 webkit_web_history_item_copy(item
));
6626 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
6630 u1
->history
= g_list_prepend(u1
->history
,
6631 webkit_web_history_item_copy(item
));
6632 items
= g_list_next(items
);
6635 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
6637 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
6638 u2
= TAILQ_LAST(&undos
, undo_tailq
);
6639 TAILQ_REMOVE(&undos
, u2
, entry
);
6641 g_list_free(u2
->history
);
6650 delete_tab(struct tab
*t
)
6654 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
6660 * no need to join thread here because it won't access t on completion
6663 TAILQ_REMOVE(&tabs
, t
, entry
);
6666 /* Halt all webkit activity. */
6667 abort_favicon_download(t
);
6668 webkit_web_view_stop_loading(t
->wv
);
6670 /* Save the tab, so we can undo the close. */
6671 undo_close_tab_save(t
);
6675 g_source_remove(t
->search_id
);
6678 bzero(&a
, sizeof a
);
6680 inspector_cmd(t
, &a
);
6682 if (browser_mode
== XT_BM_KIOSK
) {
6683 gtk_widget_destroy(t
->uri_entry
);
6684 gtk_widget_destroy(t
->stop
);
6685 gtk_widget_destroy(t
->js_toggle
);
6688 gtk_widget_destroy(t
->tab_elems
.eventbox
);
6689 gtk_widget_destroy(t
->vbox
);
6691 g_free(t
->user_agent
);
6692 g_free(t
->stylesheet
);
6697 if (TAILQ_EMPTY(&tabs
)) {
6698 if (browser_mode
== XT_BM_KIOSK
)
6699 create_new_tab(home
, NULL
, 1, -1);
6701 create_new_tab(NULL
, NULL
, 1, -1);
6704 /* recreate session */
6705 if (session_autosave
) {
6706 bzero(&a
, sizeof a
);
6708 save_tabs(NULL
, &a
);
6712 recolor_compact_tabs();
6716 update_statusbar_zoom(struct tab
*t
)
6719 char s
[16] = { '\0' };
6721 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
6722 if ((zoom
<= 0.99 || zoom
>= 1.01))
6723 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
6724 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
6728 setzoom_webkit(struct tab
*t
, int adjust
)
6730 #define XT_ZOOMPERCENT 0.04
6735 show_oops(NULL
, "setzoom_webkit invalid parameters");
6739 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
6740 if (adjust
== XT_ZOOM_IN
)
6741 zoom
+= XT_ZOOMPERCENT
;
6742 else if (adjust
== XT_ZOOM_OUT
)
6743 zoom
-= XT_ZOOMPERCENT
;
6744 else if (adjust
> 0)
6745 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
6747 show_oops(t
, "setzoom_webkit invalid zoom value");
6751 if (zoom
< XT_ZOOMPERCENT
)
6752 zoom
= XT_ZOOMPERCENT
;
6753 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
6754 update_statusbar_zoom(t
);
6758 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
6760 struct tab
*t
= (struct tab
*) data
;
6762 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
6764 switch (event
->button
) {
6766 set_current_tab(t
->tab_id
);
6777 append_tab(struct tab
*t
)
6782 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
6783 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
6787 create_sbe(int width
)
6791 sbe
= gtk_entry_new();
6792 gtk_entry_set_inner_border(GTK_ENTRY(sbe
), NULL
);
6793 gtk_entry_set_has_frame(GTK_ENTRY(sbe
), FALSE
);
6794 gtk_widget_set_can_focus(GTK_WIDGET(sbe
), FALSE
);
6795 gtk_widget_modify_font(GTK_WIDGET(sbe
), statusbar_font
);
6796 gtk_entry_set_alignment(GTK_ENTRY(sbe
), 1.0);
6797 gtk_widget_set_size_request(sbe
, width
, -1);
6803 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
6808 WebKitWebHistoryItem
*item
;
6812 int sbe_p
= 0, sbe_b
= 0,
6815 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
6817 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
6818 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
6822 t
= g_malloc0(sizeof *t
);
6824 if (title
== NULL
) {
6825 title
= "(untitled)";
6829 t
->vbox
= gtk_vbox_new(FALSE
, 0);
6831 /* label + button for tab */
6832 b
= gtk_hbox_new(FALSE
, 0);
6835 #if GTK_CHECK_VERSION(2, 20, 0)
6836 t
->spinner
= gtk_spinner_new();
6838 t
->label
= gtk_label_new(title
);
6839 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
6840 gtk_widget_set_size_request(t
->label
, 100, 0);
6841 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
6842 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
6843 gtk_widget_set_size_request(b
, 130, 0);
6845 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
6846 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
6847 #if GTK_CHECK_VERSION(2, 20, 0)
6848 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
6852 if (browser_mode
== XT_BM_KIOSK
) {
6853 t
->toolbar
= create_kiosk_toolbar(t
);
6854 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
6857 t
->toolbar
= create_toolbar(t
);
6859 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
6867 t
->browser_win
= create_browser(t
);
6868 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
6870 /* oops message for user feedback */
6871 t
->oops
= gtk_entry_new();
6872 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
6873 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
6874 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
6875 gdk_color_parse(XT_COLOR_RED
, &color
);
6876 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
6877 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
6878 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
6881 t
->cmd
= gtk_entry_new();
6882 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
6883 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
6884 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
6885 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
6888 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
6890 t
->sbe
.statusbar
= gtk_entry_new();
6891 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
6892 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
6893 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
6894 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
6896 /* create these widgets only if specified in statusbar_elems */
6898 t
->sbe
.position
= create_sbe(40);
6899 t
->sbe
.zoom
= create_sbe(40);
6900 t
->sbe
.buffercmd
= create_sbe(60);
6902 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
6904 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
6907 /* gtk widgets cannot be added to a box twice. sbe_* variables
6908 make sure of this */
6909 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
6913 GtkWidget
*sep
= gtk_vseparator_new();
6915 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
6916 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
6917 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
6918 FALSE
, FALSE
, FALSE
);
6923 warnx("flag \"%c\" specified more than "
6924 "once in statusbar_elems\n", *p
);
6928 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
6929 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
6933 warnx("flag \"%c\" specified more than "
6934 "once in statusbar_elems\n", *p
);
6938 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
6939 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
6943 warnx("flag \"%c\" specified more than "
6944 "once in statusbar_elems\n", *p
);
6948 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
6949 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
6952 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
6957 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
6960 t
->buffers
= create_buffers(t
);
6961 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
6963 /* xtp meaning is normal by default */
6964 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6966 /* set empty favicon */
6967 xt_icon_from_name(t
, "text-html");
6969 /* and show it all */
6970 gtk_widget_show_all(b
);
6971 gtk_widget_show_all(t
->vbox
);
6973 /* compact tab bar */
6974 t
->tab_elems
.label
= gtk_label_new(title
);
6975 t
->tab_elems
.favicon
= gtk_image_new();
6976 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
6977 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
6978 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
6979 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
6981 t
->tab_elems
.eventbox
= gtk_event_box_new();
6982 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
6983 t
->tab_elems
.sep
= gtk_vseparator_new();
6985 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
6986 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
6987 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
6988 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
6989 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
6990 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
6992 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.favicon
, FALSE
,
6994 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
6996 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
6998 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
7001 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
7003 gtk_widget_show_all(t
->tab_elems
.eventbox
);
7005 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7008 id
= position
>= 0 ? position
:
7009 gtk_notebook_get_current_page(notebook
) + 1;
7010 if (id
> gtk_notebook_get_n_pages(notebook
))
7013 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7014 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
7015 gtk_box_reorder_child(GTK_BOX(tab_bar
),
7016 t
->tab_elems
.eventbox
, id
);
7021 #if GTK_CHECK_VERSION(2, 20, 0)
7022 /* turn spinner off if we are a new tab without uri */
7024 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7025 gtk_widget_hide(t
->spinner
);
7028 /* make notebook tabs reorderable */
7029 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7031 /* compact tabs clickable */
7032 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
7033 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
7035 g_object_connect(G_OBJECT(t
->cmd
),
7036 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7037 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7038 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7039 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7040 "signal::populate-popup", G_CALLBACK(cmd_popup_cb
), t
,
7043 /* reuse wv_button_cb to hide oops */
7044 g_object_connect(G_OBJECT(t
->oops
),
7045 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7048 g_signal_connect(t
->buffers
,
7049 "row-activated", G_CALLBACK(row_activated_cb
), t
);
7050 g_object_connect(G_OBJECT(t
->buffers
),
7051 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, (char *)NULL
);
7053 g_object_connect(G_OBJECT(t
->wv
),
7054 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7055 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7056 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7057 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7058 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7059 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7060 "signal::resource-request-starting", G_CALLBACK(webview_rrs_cb
), t
,
7061 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7062 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7063 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7064 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7065 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7066 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7067 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7068 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
7069 "signal::populate-popup", G_CALLBACK(wv_popup_cb
), t
,
7071 g_signal_connect(t
->wv
,
7072 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7073 g_signal_connect(t
->wv
,
7074 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7076 /* hijack the unused keys as if we were the browser */
7077 //g_object_connect(G_OBJECT(t->toolbar),
7078 // "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb), t,
7081 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7082 G_CALLBACK(tab_close_cb
), t
);
7085 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7086 /* restore the tab's history */
7087 if (u
&& u
->history
) {
7091 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7092 items
= g_list_next(items
);
7095 item
= g_list_nth_data(u
->history
, u
->back
);
7097 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7100 g_list_free(u
->history
);
7102 webkit_web_back_forward_list_clear(t
->bfl
);
7108 url_set_visibility();
7109 statusbar_set_visibility();
7112 set_current_tab(t
->tab_id
);
7113 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7117 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7121 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7128 if (userstyle_global
)
7131 recolor_compact_tabs();
7132 setzoom_webkit(t
, XT_ZOOM_NORMAL
);
7137 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7143 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7145 if (gtk_notebook_get_current_page(notebook
) == -1)
7148 TAILQ_FOREACH(t
, &tabs
, entry
) {
7149 if (t
->tab_id
== pn
) {
7150 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7153 uri
= get_title(t
, TRUE
);
7154 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7160 /* can't use focus_webview here */
7161 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7168 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7171 struct tab
*t
= NULL
, *tt
;
7175 TAILQ_FOREACH(tt
, &tabs
, entry
)
7176 if (tt
->tab_id
== pn
) {
7182 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
7184 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
7189 menuitem_response(struct tab
*t
)
7191 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7195 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7197 GtkWidget
*menu
, *menu_items
;
7198 GdkEventButton
*bevent
;
7202 if (event
->type
== GDK_BUTTON_PRESS
) {
7203 bevent
= (GdkEventButton
*) event
;
7204 menu
= gtk_menu_new();
7206 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7207 if ((uri
= get_uri(ti
)) == NULL
)
7208 /* XXX make sure there is something to print */
7209 /* XXX add gui pages in here to look purdy */
7211 menu_items
= gtk_menu_item_new_with_label(uri
);
7212 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
7213 gtk_widget_show(menu_items
);
7215 g_signal_connect_swapped((menu_items
),
7216 "activate", G_CALLBACK(menuitem_response
),
7220 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7221 bevent
->button
, bevent
->time
);
7223 /* unref object so it'll free itself when popped down */
7224 #if !GTK_CHECK_VERSION(3, 0, 0)
7225 /* XXX does not need unref with gtk+3? */
7226 g_object_ref_sink(menu
);
7227 g_object_unref(menu
);
7230 return (TRUE
/* eat event */);
7233 return (FALSE
/* propagate */);
7237 icon_size_map(int iconsz
)
7239 if (iconsz
<= GTK_ICON_SIZE_INVALID
||
7240 iconsz
> GTK_ICON_SIZE_DIALOG
)
7241 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7247 create_button(char *name
, char *stockid
, int size
)
7249 GtkWidget
*button
, *image
;
7253 rcstring
= g_strdup_printf(
7254 "style \"%s-style\"\n"
7256 " GtkWidget::focus-padding = 0\n"
7257 " GtkWidget::focus-line-width = 0\n"
7261 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
7262 gtk_rc_parse_string(rcstring
);
7264 button
= gtk_button_new();
7265 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7266 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
7268 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7269 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7270 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7271 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7272 gtk_widget_set_name(button
, name
);
7273 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7279 button_set_stockid(GtkWidget
*button
, char *stockid
)
7283 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7284 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7285 gtk_button_set_image(GTK_BUTTON(button
), image
);
7289 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
7292 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
7295 if (xterm_workaround
== 0)
7299 * xterm doesn't play nice with clipboards because it clears the
7300 * primary when clicked. We rely on primary being set to properly
7301 * handle middle mouse button clicks (paste). So when someone clears
7302 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
7303 * other application behavior (as in DON'T clear primary).
7306 p
= gtk_clipboard_wait_for_text(primary
);
7308 if (gdk_property_get(gdk_get_default_root_window(),
7310 gdk_atom_intern("STRING", FALSE
),
7312 1024 * 1024 /* picked out of my butt */,
7320 /* yes sir, we need to NUL the string */
7322 gtk_clipboard_set_text(primary
, p
, -1);
7336 char file
[PATH_MAX
];
7339 vbox
= gtk_vbox_new(FALSE
, 0);
7340 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7341 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7342 #if !GTK_CHECK_VERSION(3, 0, 0)
7343 /* XXX seems to be needed with gtk+2 */
7344 gtk_notebook_set_tab_hborder(notebook
, 0);
7345 gtk_notebook_set_tab_vborder(notebook
, 0);
7347 gtk_notebook_set_scrollable(notebook
, TRUE
);
7348 gtk_notebook_set_show_border(notebook
, FALSE
);
7349 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7351 abtn
= gtk_button_new();
7352 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7353 gtk_widget_set_size_request(arrow
, -1, -1);
7354 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7355 gtk_widget_set_size_request(abtn
, -1, 20);
7357 #if GTK_CHECK_VERSION(2, 20, 0)
7358 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7360 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7362 /* compact tab bar */
7363 tab_bar
= gtk_hbox_new(TRUE
, 0);
7365 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
7366 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7367 gtk_widget_set_size_request(vbox
, -1, -1);
7369 g_object_connect(G_OBJECT(notebook
),
7370 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7372 g_object_connect(G_OBJECT(notebook
),
7373 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
7374 NULL
, (char *)NULL
);
7375 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7376 G_CALLBACK(arrow_cb
), NULL
);
7378 main_window
= create_window("xxxterm");
7379 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7380 g_signal_connect(G_OBJECT(main_window
), "delete_event",
7381 G_CALLBACK(gtk_main_quit
), NULL
);
7384 for (i
= 0; i
< LENGTH(icons
); i
++) {
7385 snprintf(file
, sizeof file
, "%s" PS
"%s", resource_dir
, icons
[i
]);
7386 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7387 l
= g_list_append(l
, pb
);
7389 gtk_window_set_default_icon_list(l
);
7391 /* clipboard work around */
7392 if (xterm_workaround
)
7394 G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
7395 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
7397 gtk_widget_show_all(abtn
);
7398 gtk_widget_show_all(main_window
);
7399 notebook_tab_set_visibility();
7402 #ifndef XT_SOCKET_DISABLE
7404 send_cmd_to_socket(char *cmd
)
7407 struct sockaddr_un sa
;
7409 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7410 warnx("%s: socket", __func__
);
7414 sa
.sun_family
= AF_UNIX
;
7415 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s" PS
"%s",
7416 work_dir
, XT_SOCKET_FILE
);
7419 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7420 warnx("%s: connect", __func__
);
7424 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
7425 warnx("%s: send", __func__
);
7436 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
7439 char str
[XT_MAX_URL_LENGTH
];
7440 socklen_t t
= sizeof(struct sockaddr_un
);
7441 struct sockaddr_un sa
;
7446 gint fd
= g_io_channel_unix_get_fd(source
);
7448 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7453 if (getpeereid(s
, &uid
, &gid
) == -1) {
7457 if (uid
!= getuid() || gid
!= getgid()) {
7458 warnx("unauthorized user");
7464 warnx("not a valid user");
7468 n
= recv(s
, str
, sizeof(str
), 0);
7472 tt
= TAILQ_LAST(&tabs
, tab_list
);
7473 cmd_execute(tt
, str
);
7481 struct sockaddr_un sa
;
7483 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7484 warn("is_running: socket");
7488 sa
.sun_family
= AF_UNIX
;
7489 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s" PS
"%s",
7490 work_dir
, XT_SOCKET_FILE
);
7493 /* connect to see if there is a listener */
7494 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7495 rv
= 0; /* not running */
7497 rv
= 1; /* already running */
7508 struct sockaddr_un sa
;
7510 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7511 warn("build_socket: socket");
7515 sa
.sun_family
= AF_UNIX
;
7516 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s" PS
"%s",
7517 work_dir
, XT_SOCKET_FILE
);
7520 /* connect to see if there is a listener */
7521 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7522 /* no listener so we will */
7523 unlink(sa
.sun_path
);
7525 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7526 warn("build_socket: bind");
7530 if (listen(s
, 1) == -1) {
7531 warn("build_socket: listen");
7549 if (stat(dir
, &sb
)) {
7550 #if defined __MINGW32__
7551 printf("making: %s\n", dir
);
7552 if (mkdir(dir
) == -1)
7554 if (mkdir(dir
, S_IRWXU
) == -1)
7556 err(1, "mkdir %s", dir
);
7558 err(1, "stat %s", dir
);
7560 if (S_ISDIR(sb
.st_mode
) == 0)
7561 errx(1, "%s not a dir", dir
);
7562 #if !defined __MINGW32__
7563 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7564 warnx("fixing invalid permissions on %s", dir
);
7565 if (chmod(dir
, S_IRWXU
) == -1)
7566 err(1, "chmod %s", dir
);
7575 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
7579 GStaticRecMutex my_gdk_mtx
= G_STATIC_REC_MUTEX_INIT
;
7580 volatile int mtx_depth
;
7584 * The linux flash plugin violates the gdk locking mechanism.
7585 * Work around the issue by using a recursive mutex with some match applied
7586 * to see if we hit a buggy condition.
7588 * The following code is painful so just don't read it. It really doesn't
7589 * make much sense but seems to work.
7594 g_static_rec_mutex_lock(&my_gdk_mtx
);
7597 if (mtx_depth
<= 0) {
7598 /* should not happen */
7599 show_oops(NULL
, "negative mutex locking bug, trying to "
7601 fprintf(stderr
, "negative mutex locking bug, trying to "
7603 g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
7604 g_static_rec_mutex_lock(&my_gdk_mtx
);
7609 if (mtx_depth
!= 1) {
7610 /* decrease mutext depth to 1 */
7612 g_static_rec_mutex_unlock(&my_gdk_mtx
);
7614 } while (mtx_depth
> 1);
7623 /* if mutex depth isn't 1 then something went bad */
7624 if (mtx_depth
!= 1) {
7625 x
= g_static_rec_mutex_unlock_full(&my_gdk_mtx
);
7627 /* should not happen */
7628 show_oops(NULL
, "mutex unlocking bug, trying to "
7630 fprintf(stderr
, "mutex unlocking bug, trying to "
7634 if (mtx_complain
== 0) {
7635 show_oops(NULL
, "buggy mutex implementation detected, "
7636 "work around implemented");
7637 fprintf(stderr
, "buggy mutex implementation detected, "
7638 "work around implemented");
7645 g_static_rec_mutex_unlock(&my_gdk_mtx
);
7651 startpage_add("<b>Welcome to xxxterm %s!</b><p>", version
);
7652 startpage_add("Beware that this is the final version that will use the"
7653 " xxxterm name.<br>Moving forward the browser will be called "
7654 "xombrero.<p>", version
);
7655 startpage_add("Details will soon appear on the "
7656 "<a href=https://opensource.conformal.com/wiki/xombrero>xombrero "
7657 "wiki page</a><p>");
7658 startpage_add("Unfortunately scroogle has shut it's doors and due to "
7659 "that one has to edit search_string in ~/.xxxterm.conf.<br>"
7660 "There are various examples in the configuration file.<br>"
7661 "The authors of xxxterm are not in a position to suggest a search "
7666 main(int argc
, char **argv
)
7669 int c
, optn
= 0, opte
= 0, focus
= 1;
7670 char conf
[PATH_MAX
] = { '\0' };
7671 char file
[PATH_MAX
];
7672 char sodversion
[32];
7673 char *env_proxy
= NULL
;
7677 start_argv
= (char * const *)argv
;
7681 g_thread_init(NULL
);
7682 gdk_threads_set_lock_functions(mtx_lock
, mtx_unlock
);
7684 gdk_threads_enter();
7686 gcry_control (GCRYCTL_SET_THREAD_CBS
, &gcry_threads_pthread
);
7688 gtk_init(&argc
, &argv
);
7690 gnutls_global_init();
7692 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
7697 RB_INIT(&downloads
);
7700 TAILQ_INIT(&sessions
);
7703 TAILQ_INIT(&aliases
);
7709 TAILQ_INIT(&ua_list
);
7711 #ifndef XT_RESOURCE_LIMITS_DISABLE
7715 GIOChannel
*channel
;
7717 /* fiddle with ulimits */
7718 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
7721 /* just use them all */
7722 rlp
.rlim_cur
= rlp
.rlim_max
;
7723 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
7725 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
7727 else if (rlp
.rlim_cur
< 1024)
7728 startpage_add("%s requires at least 1024 "
7729 "(2048 recommended) file " "descriptors, "
7730 "currently it has up to %d available",
7731 __progname
, rlp
.rlim_cur
);
7735 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
7744 #ifdef XXXTERM_BUILDSTR
7745 errx(0 , "Version: %s Build: %s",
7746 version
, XXXTERM_BUILDSTR
);
7748 errx(0 , "Version: %s", version
);
7752 strlcpy(conf
, optarg
, sizeof(conf
));
7755 strlcpy(named_session
, optarg
, sizeof(named_session
));
7776 xtp_generate_keys();
7778 /* XXX this hammer is way too big but treat all signaling the same */
7779 #ifndef XT_SIGNALS_DISABLE
7781 struct sigaction sact
;
7783 bzero(&sact
, sizeof(sact
));
7784 sigemptyset(&sact
.sa_mask
);
7785 sact
.sa_handler
= sigchild
;
7786 sact
.sa_flags
= SA_NOCLDSTOP
;
7787 sigaction(SIGCHLD
, &sact
, NULL
);
7790 /* set download dir */
7791 pwd
= getpwuid(getuid());
7793 errx(1, "invalid user %d", getuid());
7794 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
7796 /* compile buffer command regexes */
7799 /* set default string settings */
7800 home
= g_strdup("https://www.cyphertite.com");
7801 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
7802 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
7803 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
7804 cmd_font_name
= g_strdup("monospace normal 9");
7805 oops_font_name
= g_strdup("monospace normal 9");
7806 statusbar_font_name
= g_strdup("monospace normal 9");
7807 tabbar_font_name
= g_strdup("monospace normal 9");
7808 statusbar_elems
= g_strdup("BP");
7809 spell_check_languages
= g_strdup("en_US");
7810 encoding
= g_strdup("UTF-8");
7812 /* read config file */
7813 if (strlen(conf
) == 0)
7814 snprintf(conf
, sizeof conf
, "%s" PS
".%s",
7815 pwd
->pw_dir
, XT_CONF_FILE
);
7816 config_parse(conf
, 0);
7819 cmd_font
= pango_font_description_from_string(cmd_font_name
);
7820 oops_font
= pango_font_description_from_string(oops_font_name
);
7821 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
7822 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
7824 /* working directory */
7825 if (strlen(work_dir
) == 0)
7826 snprintf(work_dir
, sizeof work_dir
, "%s" PS
"%s",
7827 pwd
->pw_dir
, XT_DIR
);
7830 /* icon cache dir */
7831 snprintf(cache_dir
, sizeof cache_dir
, "%s" PS
"%s", work_dir
, XT_CACHE_DIR
);
7835 snprintf(certs_dir
, sizeof certs_dir
, "%s" PS
"%s", work_dir
, XT_CERT_DIR
);
7839 snprintf(sessions_dir
, sizeof sessions_dir
, "%s" PS
"%s",
7840 work_dir
, XT_SESSIONS_DIR
);
7841 xxx_dir(sessions_dir
);
7844 snprintf(js_dir
, sizeof js_dir
, "%s" PS
"%s", work_dir
, XT_JS_DIR
);
7848 snprintf(temp_dir
, sizeof temp_dir
, "%s" PS
"%s", work_dir
, XT_TEMP_DIR
);
7851 /* runtime settings that can override config file */
7852 if (runtime_settings
[0] != '\0')
7853 config_parse(runtime_settings
, 1);
7856 if (!strcmp(download_dir
, pwd
->pw_dir
))
7857 strlcat(download_dir
, PS
"downloads", sizeof download_dir
);
7858 xxx_dir(download_dir
);
7860 /* first start file */
7861 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_SOD_FILE
);
7862 if (stat(file
, &sb
)) {
7863 warnx("start of day file doesn't exist, creating it");
7864 if ((f
= fopen(file
, "w")) == NULL
)
7865 err(1, "startofday");
7866 if (fputs(version
, f
) == EOF
)
7873 if ((f
= fopen(file
, "r+")) == NULL
)
7874 err(1, "startofday");
7875 if (fgets(sodversion
, sizeof sodversion
, f
) == NULL
)
7877 sodversion
[strcspn(sodversion
, "\n")] = '\0';
7878 if (strcmp(version
, sodversion
)) {
7880 if (fputs(version
, f
) == EOF
)
7883 /* upgrade, say something smart */
7889 /* favorites file */
7890 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_FAVS_FILE
);
7891 if (stat(file
, &sb
)) {
7892 warnx("favorites file doesn't exist, creating it");
7893 if ((f
= fopen(file
, "w")) == NULL
)
7894 err(1, "favorites");
7898 /* quickmarks file */
7899 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_QMARKS_FILE
);
7900 if (stat(file
, &sb
)) {
7901 warnx("quickmarks file doesn't exist, creating it");
7902 if ((f
= fopen(file
, "w")) == NULL
)
7903 err(1, "quickmarks");
7907 /* search history */
7908 if (history_autosave
) {
7909 snprintf(search_file
, sizeof search_file
, "%s" PS
"%s",
7910 work_dir
, XT_SEARCH_FILE
);
7911 if (stat(search_file
, &sb
)) {
7912 warnx("search history file doesn't exist, creating it");
7913 if ((f
= fopen(search_file
, "w")) == NULL
)
7914 err(1, "search_history");
7917 history_read(&shl
, search_file
, &search_history_count
);
7920 /* command history */
7921 if (history_autosave
) {
7922 snprintf(command_file
, sizeof command_file
, "%s" PS
"%s",
7923 work_dir
, XT_COMMAND_FILE
);
7924 if (stat(command_file
, &sb
)) {
7925 warnx("command history file doesn't exist, creating it");
7926 if ((f
= fopen(command_file
, "w")) == NULL
)
7927 err(1, "command_history");
7930 history_read(&chl
, command_file
, &cmd_history_count
);
7934 session
= webkit_get_default_session();
7939 if (stat(ssl_ca_file
, &sb
)) {
7940 warnx("no CA file: %s", ssl_ca_file
);
7941 g_free(ssl_ca_file
);
7944 g_object_set(session
,
7945 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
7946 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
7950 /* guess_search regex */
7951 if (url_regex
== NULL
)
7952 url_regex
= g_strdup(XT_URL_REGEX
);
7954 if (regcomp(&url_re
, url_regex
, REG_EXTENDED
| REG_NOSUB
))
7955 startpage_add("invalid url regex %s", url_regex
);
7958 env_proxy
= getenv("http_proxy");
7960 setup_proxy(env_proxy
);
7962 env_proxy
= getenv("HTTP_PROXY");
7964 setup_proxy(env_proxy
);
7966 setup_proxy(http_proxy
);
7968 #ifndef XT_SOCKET_DISABLE
7970 send_cmd_to_socket(argv
[0]);
7974 opte
= opte
; /* shut mingw up */
7976 /* set some connection parameters */
7977 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
7978 g_object_set(session
, "max-conns-per-host", max_host_connections
,
7981 g_signal_connect(session
, "request-queued", G_CALLBACK(session_rq_cb
),
7984 #ifndef XT_SOCKET_DISABLE
7985 /* see if there is already an xxxterm running */
7986 if (single_instance
&& is_running()) {
7988 warnx("already running");
7993 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
7994 send_cmd_to_socket(cmd
);
8004 optn
= optn
; /* shut mingw up */
8009 if (enable_strict_transport
)
8010 strict_transport_init();
8012 /* uri completion */
8013 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8016 buffers_store
= gtk_list_store_new
8017 (NUM_COLS
, G_TYPE_UINT
, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
8023 notebook_tab_set_visibility();
8025 if (save_global_history
)
8026 restore_global_history();
8028 /* restore session list */
8029 restore_sessions_list();
8031 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8032 restore_saved_tabs();
8034 a
.s
= named_session
;
8035 a
.i
= XT_SES_DONOTHING
;
8036 open_tabs(NULL
, &a
);
8039 /* see if we have an exception */
8040 if (!TAILQ_EMPTY(&spl
)) {
8041 create_new_tab("about:startpage", NULL
, focus
, -1);
8046 create_new_tab(argv
[0], NULL
, focus
, -1);
8053 if (TAILQ_EMPTY(&tabs
))
8054 create_new_tab(home
, NULL
, 1, -1);
8055 #ifndef XT_SOCKET_DISABLE
8057 if ((s
= build_socket()) != -1) {
8058 channel
= g_io_channel_unix_new(s
);
8059 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
8066 gdk_threads_leave();
8067 g_static_rec_mutex_unlock_full(&my_gdk_mtx
); /* just in case */
8070 gnutls_global_deinit();