6 #include <webkit2/webkit2.h>
12 struct Configuration cfg;
17 int cooperative_pipe_fp = 0;
20 size_t num_closed = 0;
23 isearch_counted_matches(GtkWidget *widget, guint matches, gpointer data)
25 struct Client *c = (struct Client *)data;
26 char *text = malloc(12);
27 sprintf(text, "%d matches", matches);
28 gtk_label_set_text(GTK_LABEL(c->isearch_matches), text);
34 quit_if_nothing_active(void)
41 downloadmanager_show();
47 remote_msg(GIOChannel *channel, GIOCondition condition, gpointer data)
50 g_io_channel_read_line(channel, &uri, NULL, NULL, NULL);
53 client_new(uri, NULL);
62 fprintf(stderr, "chorizo: fatal: alloc failed\n");
67 client_destroy(GtkWidget *widget, gpointer data)
69 struct Client *c = (struct Client *)data;
71 g_signal_handlers_disconnect_by_func(G_OBJECT(c->web_view),
72 changed_load_progress, c);
74 idx = gtk_notebook_page_num(GTK_NOTEBOOK(mw.notebook), c->vbox);
76 fprintf(stderr, "chorizo: warning: tab index was -1\n");
78 gtk_notebook_remove_page(GTK_NOTEBOOK(mw.notebook), idx);
80 if (!cfg.private && WEBKIT_IS_WEB_VIEW(c->web_view)) {
82 webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
84 // TODO: Shift everything left if over certain amount
87 if (num_closed > cfg_max_tabs_closed) {
88 memmove(closed_tabs, closed_tabs,
89 cfg_max_tabs_closed - 1);
90 num_closed = cfg_max_tabs_closed;
92 closed_tabs = realloc(
94 num_closed * sizeof(closed_tabs[0]));
95 if (!closed_tabs) allocfail();
97 closed_tabs[num_closed - 1] = strdup(uri);
104 quit_if_nothing_active();
108 set_uri(const char *uri, struct Client *c)
110 if (!gtk_widget_is_focus(c->location))
111 gtk_entry_set_text(GTK_ENTRY(c->location),
112 (uri != NULL) ? uri : "");
116 client_new(const gchar *uri, WebKitWebView *related_wv)
120 GtkWidget *evbox, *tabbox;
121 if (uri != NULL && !cfg.noncooperative_instances &&
122 !cfg.cooperative_alone) {
123 f = ensure_uri_scheme(uri);
124 write(cooperative_pipe_fp, f, strlen(f));
125 write(cooperative_pipe_fp, "\n", 1);
129 c = calloc(1, sizeof(struct Client));
132 if (related_wv == NULL) {
133 WebKitUserContentManager *ucm =
134 webkit_user_content_manager_new();
135 WebKitUserScript *wkscript;
136 WebKitUserStyleSheet *wkstyle;
137 gchar *path = NULL, *source, *base;
138 const gchar *entry = NULL;
140 base = g_build_filename(g_get_user_data_dir(), "chorizo",
141 "user-scripts", NULL);
142 dir = g_dir_open(base, 0, NULL);
144 while ((entry = g_dir_read_name(dir)) != NULL) {
145 path = g_build_filename(base, entry, NULL);
146 if (g_str_has_suffix(path, ".js")) {
147 g_file_get_contents(path, &source, NULL,
149 wkscript = webkit_user_script_new(
151 WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
152 WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
154 webkit_user_content_manager_add_script(
156 webkit_user_script_unref(wkscript);
159 if (source) g_free(source);
163 base = g_build_filename(g_get_user_data_dir(), "chorizo",
164 "user-styles", NULL);
165 dir = g_dir_open(base, 0, NULL);
167 while ((entry = g_dir_read_name(dir)) != NULL) {
168 path = g_build_filename(base, entry, NULL);
169 if (g_str_has_suffix(path, ".css")) {
170 g_file_get_contents(path, &source, NULL,
172 wkstyle = webkit_user_style_sheet_new(
174 WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
175 WEBKIT_USER_STYLE_LEVEL_USER,
177 webkit_user_content_manager_add_style_sheet(
179 webkit_user_style_sheet_unref(wkstyle);
189 webkit_web_view_new_with_user_content_manager(ucm);
191 c->settings = webkit_web_view_get_settings(
192 WEBKIT_WEB_VIEW(c->web_view));
194 webkit_settings_set_enable_write_console_messages_to_stdout(
196 webkit_settings_set_enable_developer_extras(c->settings, TRUE);
198 c->web_view = webkit_web_view_new_with_related_view(related_wv);
201 g_signal_connect(G_OBJECT(c->web_view), "notify::favicon",
202 G_CALLBACK(changed_favicon), c);
203 g_signal_connect(G_OBJECT(c->web_view), "notify::title",
204 G_CALLBACK(changed_title), c);
205 g_signal_connect(G_OBJECT(c->web_view), "notify::uri",
206 G_CALLBACK(changed_uri), c);
207 g_signal_connect(G_OBJECT(c->web_view),
208 "notify::estimated-load-progress",
209 G_CALLBACK(changed_load_progress), c);
210 g_signal_connect(G_OBJECT(c->web_view), "create",
211 G_CALLBACK(client_new_request), c);
212 g_signal_connect(G_OBJECT(c->web_view), "close",
213 G_CALLBACK(client_destroy), c);
214 g_signal_connect(G_OBJECT(c->web_view), "decide-policy",
215 G_CALLBACK(decide_policy), NULL);
216 g_signal_connect(G_OBJECT(c->web_view), "key-press-event",
217 G_CALLBACK(key_web_view), c);
218 g_signal_connect(G_OBJECT(c->web_view), "scroll-event",
219 G_CALLBACK(key_web_view), c);
220 g_signal_connect(G_OBJECT(c->web_view), "mouse-target-changed",
221 G_CALLBACK(hover_web_view), c);
222 g_signal_connect(G_OBJECT(c->web_view), "web-process-crashed",
223 G_CALLBACK(crashed_web_view), c);
225 GtkWidget *locbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
227 c->location = gtk_entry_new();
228 gtk_entry_set_placeholder_text(GTK_ENTRY(c->location), "URL");
229 char *location_symbol;
231 location_symbol = "security-high-symbolic";
232 gtk_entry_set_icon_tooltip_markup(
233 GTK_ENTRY(c->location), GTK_ENTRY_ICON_PRIMARY,
234 "You are in private mode. No history, caches, or "
235 "cookies will be saved beyond this session.");
237 location_symbol = "text-x-generic-symbolic";
240 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(c->location),
241 GTK_ENTRY_ICON_PRIMARY,
243 gtk_box_pack_start(GTK_BOX(locbox), c->location, TRUE, TRUE, 0);
245 c->wsearch = gtk_entry_new();
246 gtk_entry_set_placeholder_text(GTK_ENTRY(c->wsearch), "Search the web");
247 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(c->wsearch),
248 GTK_ENTRY_ICON_PRIMARY,
249 "system-search-symbolic");
250 gtk_box_pack_start(GTK_BOX(locbox), c->wsearch, FALSE, TRUE, 0);
252 g_signal_connect(G_OBJECT(c->location), "key-press-event",
253 G_CALLBACK(key_location), c);
254 g_signal_connect(G_OBJECT(c->location), "icon-release",
255 G_CALLBACK(icon_location), c);
256 g_signal_connect(G_OBJECT(c->wsearch), "key-press-event",
257 G_CALLBACK(key_wsearch), c);
259 * XXX This is a workaround. Setting this to NULL (which is done in
260 * grab_feeds_finished() if no feed has been detected) adds a little
261 * padding left of the text. Not sure why. The point of this call
262 * right here is to have that padding right from the start. This
263 * avoids a graphical artifact.
266 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(c->location),
267 GTK_ENTRY_ICON_SECONDARY, NULL);
269 c->isearch_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
270 c->isearch = gtk_entry_new();
271 gtk_entry_set_placeholder_text(GTK_ENTRY(c->isearch), "Search page");
272 gtk_box_pack_start(GTK_BOX(c->isearch_box), c->isearch, FALSE, TRUE, 0);
273 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(c->isearch),
274 GTK_ENTRY_ICON_PRIMARY,
275 "system-search-symbolic");
276 g_signal_connect(G_OBJECT(c->isearch), "key-press-event",
277 G_CALLBACK(key_isearch), c);
279 c->isearch_matches = gtk_label_new("0 matches");
280 gtk_box_pack_start(GTK_BOX(c->isearch_box), c->isearch_matches, FALSE,
283 c->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
284 gtk_box_pack_start(GTK_BOX(c->vbox), c->web_view, TRUE, TRUE, 0);
285 gtk_box_pack_start(GTK_BOX(c->vbox), c->isearch_box, FALSE, TRUE, 0);
286 gtk_box_pack_start(GTK_BOX(c->vbox), locbox, FALSE, FALSE, 0);
288 gtk_container_set_focus_child(GTK_CONTAINER(c->vbox), c->web_view);
290 c->tabicon = gtk_image_new_from_icon_name("text-html",
291 GTK_ICON_SIZE_SMALL_TOOLBAR);
293 c->tablabel = gtk_label_new("chorizo");
294 gtk_label_set_ellipsize(GTK_LABEL(c->tablabel), PANGO_ELLIPSIZE_END);
295 gtk_label_set_width_chars(GTK_LABEL(c->tablabel), cfg_tab_width);
296 gtk_widget_set_has_tooltip(c->tablabel, TRUE);
299 * XXX I don't own a HiDPI screen, so I don't know if scale_factor
300 * does the right thing.
302 tabbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,
303 5 * gtk_widget_get_scale_factor(mw.win));
304 gtk_box_pack_start(GTK_BOX(tabbox), c->tabicon, FALSE, FALSE, 0);
305 gtk_box_pack_start(GTK_BOX(tabbox), c->tablabel, TRUE, TRUE, 0);
307 evbox = gtk_event_box_new();
308 gtk_container_add(GTK_CONTAINER(evbox), tabbox);
309 g_signal_connect(G_OBJECT(evbox), "button-release-event",
310 G_CALLBACK(key_tablabel), c);
312 gtk_widget_add_events(evbox, GDK_SCROLL_MASK);
313 g_signal_connect(G_OBJECT(evbox), "scroll-event",
314 G_CALLBACK(key_tablabel), c);
316 // For easy access, store a reference to our label.
317 g_object_set_data(G_OBJECT(evbox), "chorizo-tab-label", c->tablabel);
320 * This only shows the event box and the label inside, nothing else.
321 * Needed because the evbox/label is "internal" to the notebook and
322 * not part of the normal "widget tree" (IIUC).
324 gtk_widget_show_all(evbox);
326 int page = gtk_notebook_get_current_page(GTK_NOTEBOOK(mw.notebook)) + 1;
327 gtk_notebook_insert_page(GTK_NOTEBOOK(mw.notebook), c->vbox, evbox,
329 gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(mw.notebook), c->vbox,
332 show_web_view(NULL, c);
335 f = ensure_uri_scheme(uri);
336 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), f);
343 if (uri == NULL) gtk_widget_grab_focus(c->location);
345 return WEBKIT_WEB_VIEW(c->web_view);
349 client_new_request(WebKitWebView *web_view,
350 WebKitNavigationAction *navigation_action, gpointer data)
353 WebKitWebView *new_web_view = client_new(NULL, web_view);
354 gtk_widget_grab_focus(GTK_WIDGET(new_web_view));
359 mkdirp(const char *dir, mode_t mode)
364 snprintf(tmp, sizeof(tmp), "%s", dir);
366 if (tmp[len - 1] == '/') tmp[len - 1] = 0;
367 for (p = tmp + 1; *p; p++)
377 cooperation_setup(void)
382 gchar *priv = (cfg.private) ? "-private" : "";
383 const gchar *fifo_suffix_env = g_getenv("CHORIZO_FIFO_SUFFIX");
384 const gchar *fifo_suffix = (fifo_suffix_env) ? fifo_suffix_env : "";
385 fifofilename = g_strdup_printf("%s%s%s%s", "chorizo", priv, ".fifo",
387 fifopath = g_build_filename(g_get_user_runtime_dir(), "chorizo",
389 mkdirp(dirname(fifopath), 0600);
390 g_free(fifofilename);
392 if (!g_file_test(fifopath, G_FILE_TEST_EXISTS)) mkfifo(fifopath, 0600);
394 cooperative_pipe_fp = open(fifopath, O_WRONLY | O_NONBLOCK);
395 if (!cooperative_pipe_fp) {
396 fprintf(stderr, "chorizo: error: can't open FIFO\n");
398 if (write(cooperative_pipe_fp, "", 0) == -1) {
400 * Could not do an empty write to the FIFO which
401 * means there's no one listening.
403 close(cooperative_pipe_fp);
404 towatch = g_io_channel_new_file(fifopath, "r+", NULL);
405 g_io_add_watch(towatch, G_IO_IN, (GIOFunc)remote_msg,
408 cfg.cooperative_alone = FALSE;
414 changed_load_progress(GObject *obj, GParamSpec *pspec, gpointer data)
416 struct Client *c = (struct Client *)data;
419 "a = document.querySelectorAll('"
421 "link[rel=\"alternate\"][href][type=\"application/atom+xml\"],"
423 "link[rel=\"alternate\"][href][type=\"application/rss+xml\"]"
429 " for (i = 0; i < a.length; i++) {"
430 " url = encodeURIComponent(a[i].href);"
431 " if ('title' in a[i] && a[i].title != '')"
432 " title = encodeURIComponent(a[i].title);"
435 " out += '<li><a href=\"' + url + '\">' + title + "
440 p = webkit_web_view_get_estimated_load_progress(
441 WEBKIT_WEB_VIEW(c->web_view));
446 * The page has loaded fully. We now run the short JavaScript
447 * snippet above that operates on the DOM. It tries to grab
448 * all occurences of <link rel="alternate" ...>, i.e.
449 * RSS/Atom feed references.
451 webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(c->web_view),
453 grab_feeds_finished, c);
455 gtk_entry_set_progress_fraction(GTK_ENTRY(c->location), p);
459 changed_favicon(GObject *obj, GParamSpec *pspec, gpointer data)
461 struct Client *c = (struct Client *)data;
463 int w, h, w_should, h_should;
464 GdkPixbuf *pb, *pb_scaled;
465 f = webkit_web_view_get_favicon(WEBKIT_WEB_VIEW(c->web_view));
467 gtk_image_set_from_icon_name(GTK_IMAGE(c->tabicon), "text-html",
468 GTK_ICON_SIZE_SMALL_TOOLBAR);
470 w = cairo_image_surface_get_width(f);
471 h = cairo_image_surface_get_height(f);
472 pb = gdk_pixbuf_get_from_surface(f, 0, 0, w, h);
474 w_should = 16 * gtk_widget_get_scale_factor(c->tabicon);
475 h_should = 16 * gtk_widget_get_scale_factor(c->tabicon);
476 pb_scaled = gdk_pixbuf_scale_simple(
477 pb, w_should, h_should, GDK_INTERP_BILINEAR);
478 gtk_image_set_from_pixbuf(GTK_IMAGE(c->tabicon),
481 g_object_unref(pb_scaled);
488 changed_title(GObject *obj, GParamSpec *pspec, gpointer data)
491 struct Client *c = (struct Client *)data;
492 u = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
493 t = webkit_web_view_get_title(WEBKIT_WEB_VIEW(c->web_view));
495 u = u == NULL ? "chorizo" : u;
496 u = u[0] == 0 ? "chorizo" : u;
498 t = t == NULL ? u : t;
499 t = t[0] == 0 ? u : t;
501 gchar *name = malloc(strlen(t) + 4);
502 if (!name) allocfail();
504 webkit_web_view_get_is_muted(WEBKIT_WEB_VIEW(c->web_view));
505 gchar *muted = (mute) ? "[M] " : "";
506 sprintf(name, "%s%s", muted, t);
507 gtk_label_set_text(GTK_LABEL(c->tablabel), name);
510 gtk_widget_set_tooltip_text(c->tablabel, t);
512 gtk_notebook_get_current_page(GTK_NOTEBOOK(mw.notebook)));
516 changed_uri(GObject *obj, GParamSpec *pspec, gpointer data)
519 struct Client *c = (struct Client *)data;
521 t = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
523 if (t != NULL && strlen(t) > 0) {
526 // No g_get_user_state_dir unfortunately
527 gchar *state_env = getenv("XDG_STATE_DIR");
528 gchar *state_dir = (state_env) ?
530 g_build_filename(g_get_home_dir(),
534 gchar *history_file =
535 g_build_filename(state_dir, "history", NULL);
537 mkdirp(state_dir, 0700);
538 fp = fopen(history_file, "a");
540 fprintf(fp, "%s\n", t);
543 perror("chorizo: error: could not open history file");
546 g_free(history_file);
552 crashed_web_view(WebKitWebView *web_view, gpointer data)
554 GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
555 GtkWidget *dialog = gtk_message_dialog_new(
556 GTK_WINDOW(mw.win), flags, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
557 "ERROR: Web process %s crashed.\n%s",
558 webkit_web_view_get_uri(WEBKIT_WEB_VIEW(web_view)),
560 gtk_dialog_run(GTK_DIALOG(dialog));
561 gtk_widget_destroy(dialog);
567 decide_policy(WebKitWebView *web_view, WebKitPolicyDecision *decision,
568 WebKitPolicyDecisionType type, gpointer data)
570 WebKitResponsePolicyDecision *r;
572 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
573 r = WEBKIT_RESPONSE_POLICY_DECISION(decision);
574 if (!webkit_response_policy_decision_is_mime_type_supported(r))
575 webkit_policy_decision_download(decision);
577 webkit_policy_decision_use(decision);
580 // Use whatever default there is.
587 ensure_uri_scheme(const gchar *t)
590 f = g_ascii_strdown(t, -1);
591 if (!g_str_has_prefix(f, "http:") && !g_str_has_prefix(f, "https:") &&
592 !g_str_has_prefix(f, "file:") && !g_str_has_prefix(f, "about:") &&
593 !g_str_has_prefix(f, "data:") && !g_str_has_prefix(f, "webkit:")) {
595 fabs = realpath(t, NULL);
597 f = g_strdup_printf("file://%s", fabs);
600 f = g_strdup_printf("http://%s", t);
608 grab_feeds_finished(GObject *object, GAsyncResult *result, gpointer data)
610 struct Client *c = (struct Client *)data;
611 WebKitJavascriptResult *js_result;
613 JSCException *exception;
616 g_free(c->feed_html);
620 * This was taken almost verbatim from the example in WebKit's
623 * https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html
626 js_result = webkit_web_view_run_javascript_finish(
627 WEBKIT_WEB_VIEW(object), result, &err);
630 "chorizo: error: error running javascript: %s\n",
635 value = webkit_javascript_result_get_js_value(js_result);
636 if (jsc_value_is_string(value)) {
637 str_value = jsc_value_to_string(value);
639 jsc_context_get_exception(jsc_value_get_context(value));
640 if (exception != NULL) {
642 "chorizo: warning: error running javascript: %s\n",
643 jsc_exception_get_message(exception));
645 c->feed_html = str_value;
648 gtk_entry_set_icon_from_icon_name(
649 GTK_ENTRY(c->location), GTK_ENTRY_ICON_SECONDARY,
650 "application-rss+xml-symbolic");
651 gtk_entry_set_icon_activatable(GTK_ENTRY(c->location),
652 GTK_ENTRY_ICON_SECONDARY, TRUE);
654 gtk_entry_set_icon_from_icon_name(
655 GTK_ENTRY(c->location), GTK_ENTRY_ICON_SECONDARY, NULL);
658 webkit_javascript_result_unref(js_result);
662 hover_web_view(WebKitWebView *web_view, WebKitHitTestResult *ht,
663 guint modifiers, gpointer data)
665 struct Client *c = (struct Client *)data;
667 g_free(c->hover_uri);
669 if (webkit_hit_test_result_context_is_link(ht)) {
670 to_show = webkit_hit_test_result_get_link_uri(ht);
671 c->hover_uri = g_strdup(to_show);
673 to_show = webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view));
677 if (!gtk_widget_is_focus(c->location)) set_uri(to_show, c);
681 icon_location(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event,
684 struct Client *c = (struct Client *)data;
686 gchar *data_template = "data:text/html,"
690 " <meta charset=\"UTF-8\">"
691 " <title>Feeds</title>"
694 " <p>Feeds found on this page:</p>"
700 if (c->feed_html != NULL) {
702 * What we're actually trying to do is show a simple HTML
703 * page that lists all the feeds on the current page. The
704 * function webkit_web_view_load_html() looks like the proper
705 * way to do that. Sad thing is, it doesn't create a history
706 * entry, but instead simply replaces the content of the
707 * current page. This is not what we want.
709 * RFC 2397 [0] defines the data URI scheme [1]. We abuse this
710 * mechanism to show my custom HTML snippet* and*create a
713 * [0]: https://tools.ietf.org/html/rfc2397 [1]:
714 * https://en.wikipedia.org/wiki/Data_URI_scheme
717 d = g_strdup_printf(data_template, c->feed_html);
718 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(c->web_view), d);
724 init_default_web_context(void)
727 WebKitWebContext *wc;
728 WebKitCookieManager *cm;
729 wc = (cfg.private) ? webkit_web_context_new_ephemeral() :
730 webkit_web_context_get_default();
732 p = g_build_filename(g_get_user_config_dir(), "chorizo", "adblock",
734 webkit_web_context_set_sandbox_enabled(wc, TRUE);
735 webkit_web_context_add_path_to_sandbox(wc, p, TRUE);
738 webkit_web_context_set_process_model(
739 wc, WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
741 p = g_build_filename(g_get_user_data_dir(), "chorizo", "web-extensions",
743 webkit_web_context_set_web_extensions_directory(wc, p);
746 char *xdg_down = getenv("XDG_DOWNLOAD_DIR");
747 g_signal_connect(G_OBJECT(wc), "download-started",
748 G_CALLBACK(download_start),
749 (xdg_down) ? xdg_down : "/var/tmp");
751 trust_user_certs(wc);
753 cm = webkit_web_context_get_cookie_manager(wc);
754 webkit_cookie_manager_set_accept_policy(cm, cfg_cookie_policy);
757 webkit_web_context_set_favicon_database_directory(wc, NULL);
759 gchar *fname = g_build_filename("/", g_get_user_data_dir(),
760 "chorizo", "cookies.db", NULL);
761 mkdirp(dirname(fname), 0700);
762 WebKitCookiePersistentStorage type =
763 WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE;
764 webkit_cookie_manager_set_persistent_storage(cm, fname, type);
767 webkit_web_context_set_spell_checking_enabled(wc, TRUE);
771 isearch(gpointer data, gint direction)
773 struct Client *c = (struct Client *)data;
774 WebKitWebView *web_view = WEBKIT_WEB_VIEW(c->web_view);
775 WebKitFindController *fc =
776 webkit_web_view_get_find_controller(web_view);
777 const gchar *isearch_text = gtk_entry_get_text(GTK_ENTRY(c->isearch));
778 if (isearch_text == NULL) return;
782 g_signal_connect(G_OBJECT(fc), "counted-matches",
783 G_CALLBACK(isearch_counted_matches), c);
784 webkit_find_controller_count_matches(
785 fc, isearch_text, cfg_isearch_options, G_MAXUINT);
786 webkit_find_controller_search(fc, isearch_text,
787 cfg_isearch_options, G_MAXUINT);
790 webkit_find_controller_search_next(fc);
793 webkit_find_controller_search_previous(fc);
796 webkit_find_controller_search_finish(fc);
802 isearch_init(struct Client *c, int direction)
804 if (webkit_web_view_get_uri(WEBKIT_WEB_VIEW(c->web_view))) {
805 gtk_widget_show(c->isearch_box);
806 gtk_widget_grab_focus(c->isearch);
813 if (num_closed == 0) return;
814 client_new(closed_tabs[num_closed - 1], NULL);
816 closed_tabs = realloc(closed_tabs, num_closed * sizeof(closed_tabs[0]));
817 if (!closed_tabs) allocfail();
821 mainwindow_setup(void)
823 mw.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
824 gtk_window_set_default_size(GTK_WINDOW(mw.win), 1200, 900);
825 g_signal_connect(G_OBJECT(mw.win), "destroy", gtk_main_quit, NULL);
827 gchar *priv = (cfg.private) ? "-private" : "";
828 gchar *title = malloc(strlen(priv) + 7);
829 if (!title) allocfail();
830 sprintf(title, "%s%s", "chorizo", priv);
831 gtk_window_set_title(GTK_WINDOW(mw.win), title);
834 mw.notebook = gtk_notebook_new();
835 gtk_notebook_set_scrollable(GTK_NOTEBOOK(mw.notebook), TRUE);
836 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(mw.notebook), GTK_POS_LEFT);
837 gtk_container_add(GTK_CONTAINER(mw.win), mw.notebook);
838 g_signal_connect(G_OBJECT(mw.notebook), "switch-page",
839 G_CALLBACK(notebook_switch_page), NULL);
841 GtkCssProvider *css = gtk_css_provider_new();
842 const char *css_data = "notebook header.left * { \
847 gtk_css_provider_load_from_data(css, css_data, strlen(css_data), NULL);
848 gtk_style_context_add_provider_for_screen(
849 gdk_screen_get_default(), GTK_STYLE_PROVIDER(css),
850 GTK_STYLE_PROVIDER_PRIORITY_USER);
852 gtk_widget_show_all(mw.win);
856 mainwindow_title(gint idx)
858 GtkWidget *child, *widg, *tablabel;
860 child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(mw.notebook), idx);
861 if (child == NULL) return;
863 widg = gtk_notebook_get_tab_label(GTK_NOTEBOOK(mw.notebook), child);
864 tablabel = (GtkWidget *)g_object_get_data(G_OBJECT(widg),
865 "chorizo-tab-label");
866 text = gtk_label_get_text(GTK_LABEL(tablabel));
867 gtk_window_set_title(GTK_WINDOW(mw.win), text);
871 notebook_switch_page(GtkNotebook *nb, GtkWidget *p, guint idx, gpointer data)
873 mainwindow_title(idx);
877 show_web_view(WebKitWebView *web_view, gpointer data)
879 struct Client *c = (struct Client *)data;
881 gint idx = gtk_notebook_page_num(GTK_NOTEBOOK(mw.notebook), c->vbox);
883 gtk_widget_show_all(GTK_WIDGET(
884 gtk_notebook_get_nth_page(GTK_NOTEBOOK(mw.notebook), idx)));
885 gtk_widget_hide(c->isearch_box);
887 if (idx != -1 && switch_tab)
888 gtk_notebook_set_current_page(GTK_NOTEBOOK(mw.notebook), idx);
889 gtk_widget_grab_focus(c->web_view);
893 trust_user_certs(WebKitWebContext *wc)
895 GTlsCertificate *cert;
896 gchar *basedir, *absfile;
899 basedir = g_build_filename(g_get_user_data_dir(), "chorizo", "certs",
901 dir = g_dir_open(basedir, 0, NULL);
904 file = g_dir_read_name(dir);
905 while (file != NULL) {
906 absfile = g_build_filename(g_get_user_data_dir(),
907 "chorizo", "certs", file,
909 cert = g_tls_certificate_new_from_file(absfile, NULL);
913 "chorizo: warning: could not load trusted cert: %s\n",
916 webkit_web_context_allow_tls_certificate_for_host(
918 file = g_dir_read_name(dir);
927 printf("%s %s\n", "chorizo", VERSION);
931 main(int argc, char **argv)
936 cfg.noncooperative_instances = FALSE;
937 cfg.cooperative_alone = TRUE;
938 closed_tabs = malloc(0);
939 if (!closed_tabs) allocfail();
941 while ((opt = getopt(argc, argv, "cpvV")) != -1) {
944 cfg.noncooperative_instances = TRUE;
957 "usage: chorizo [OPTION]... [URI]...\n");
962 if (cfg.verbose) version();
964 gtk_init(&argc, &argv);
966 // Keep clipboard contents after program closes
967 gtk_clipboard_store(gtk_clipboard_get_for_display(
968 gdk_display_get_default(), GDK_SELECTION_CLIPBOARD));
970 if (!cfg.noncooperative_instances) cooperation_setup();
972 if (cfg.noncooperative_instances || cfg.cooperative_alone)
973 init_default_web_context();
975 downloadmanager_setup();
978 if (optind >= argc) {
979 client_new(cfg_home_uri, NULL);
981 for (i = optind; i < argc; i++) client_new(argv[i], NULL);
984 if (cfg.noncooperative_instances || cfg.cooperative_alone) {