+gboolean
+winevent(GtkWidget *w, GdkEvent *e, Client *c)
+{
+ int i;
+
+ switch (e->type) {
+ case GDK_ENTER_NOTIFY:
+ c->overtitle = c->targeturi;
+ updatetitle(c);
+ break;
+ case GDK_KEY_PRESS:
+ if (!curconfig[KioskMode].val.i) {
+ for (i = 0; i < LENGTH(keys); ++i) {
+ if (gdk_keyval_to_lower(e->key.keyval) ==
+ keys[i].keyval &&
+ CLEANMASK(e->key.state) == keys[i].mod &&
+ keys[i].func) {
+ updatewinid(c);
+ keys[i].func(c, &(keys[i].arg));
+ return TRUE;
+ }
+ }
+ }
+ case GDK_LEAVE_NOTIFY:
+ c->overtitle = NULL;
+ updatetitle(c);
+ break;
+ case GDK_WINDOW_STATE:
+ if (e->window_state.changed_mask ==
+ GDK_WINDOW_STATE_FULLSCREEN)
+ c->fullscreen = e->window_state.new_window_state &
+ GDK_WINDOW_STATE_FULLSCREEN;
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+void
+showview(WebKitWebView *v, Client *c)
+{
+ GdkRGBA bgcolor = { 0 };
+ GdkWindow *gwin;
+
+ c->finder = webkit_web_view_get_find_controller(c->view);
+ c->inspector = webkit_web_view_get_inspector(c->view);
+
+ c->win = createwindow(c);
+
+ gtk_container_add(GTK_CONTAINER(c->win), GTK_WIDGET(c->view));
+ gtk_widget_show_all(c->win);
+ gtk_widget_grab_focus(GTK_WIDGET(c->view));
+
+ gwin = gtk_widget_get_window(GTK_WIDGET(c->win));
+ c->xid = gdk_x11_window_get_xid(gwin);
+ updatewinid(c);
+ if (showxid) {
+ gdk_display_sync(gtk_widget_get_display(c->win));
+ puts(winid);
+ }
+
+ if (curconfig[HideBackground].val.i)
+ webkit_web_view_set_background_color(c->view, &bgcolor);
+
+ if (!curconfig[KioskMode].val.i) {
+ gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK);
+ gdk_window_add_filter(gwin, processx, c);
+ }
+
+ if (curconfig[RunInFullscreen].val.i)
+ togglefullscreen(c, NULL);
+
+ if (curconfig[ZoomLevel].val.f != 1.0)
+ webkit_web_view_set_zoom_level(c->view,
+ curconfig[ZoomLevel].val.f);
+
+ setatom(c, AtomFind, "");
+ setatom(c, AtomUri, "about:blank");
+}
+
+GtkWidget *
+createwindow(Client *c)
+{
+ char *wmstr;
+ GtkWidget *w;
+
+ if (embed) {
+ w = gtk_plug_new(embed);
+ } else {
+ w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ wmstr = g_path_get_basename(argv0);
+ gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf");
+ g_free(wmstr);
+
+ wmstr = g_strdup_printf("%s[%lu]", "Surf",
+ webkit_web_view_get_page_id(c->view));
+ gtk_window_set_role(GTK_WINDOW(w), wmstr);
+ g_free(wmstr);
+
+ gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]);
+ }
+
+ g_signal_connect(G_OBJECT(w), "destroy",
+ G_CALLBACK(destroywin), c);
+ g_signal_connect(G_OBJECT(w), "enter-notify-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "key-press-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "leave-notify-event",
+ G_CALLBACK(winevent), c);
+ g_signal_connect(G_OBJECT(w), "window-state-event",
+ G_CALLBACK(winevent), c);
+
+ return w;
+}
+
+gboolean
+loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert,
+ GTlsCertificateFlags err, Client *c)
+{
+ GString *errmsg = g_string_new(NULL);
+ gchar *html, *pem;
+
+ c->failedcert = g_object_ref(cert);
+ c->tlserr = err;
+ c->errorpage = 1;
+
+ if (err & G_TLS_CERTIFICATE_UNKNOWN_CA)
+ g_string_append(errmsg,
+ "The signing certificate authority is not known.<br>");
+ if (err & G_TLS_CERTIFICATE_BAD_IDENTITY)
+ g_string_append(errmsg,
+ "The certificate does not match the expected identity "
+ "of the site that it was retrieved from.<br>");
+ if (err & G_TLS_CERTIFICATE_NOT_ACTIVATED)
+ g_string_append(errmsg,
+ "The certificate's activation time "
+ "is still in the future.<br>");
+ if (err & G_TLS_CERTIFICATE_EXPIRED)
+ g_string_append(errmsg, "The certificate has expired.<br>");
+ if (err & G_TLS_CERTIFICATE_REVOKED)
+ g_string_append(errmsg,
+ "The certificate has been revoked according to "
+ "the GTlsConnection's certificate revocation list.<br>");
+ if (err & G_TLS_CERTIFICATE_INSECURE)
+ g_string_append(errmsg,
+ "The certificate's algorithm is considered insecure.<br>");
+ if (err & G_TLS_CERTIFICATE_GENERIC_ERROR)
+ g_string_append(errmsg,
+ "Some error occurred validating the certificate.<br>");
+
+ g_object_get(cert, "certificate-pem", &pem, NULL);
+ html = g_strdup_printf("<p>Could not validate TLS for “%s”<br>%s</p>"
+ "<p>You can inspect the following certificate "
+ "with Ctrl-t (default keybinding).</p>"
+ "<p><pre>%s</pre></p>", uri, errmsg->str, pem);
+ g_free(pem);
+ g_string_free(errmsg, TRUE);
+
+ webkit_web_view_load_alternate_html(c->view, html, uri, NULL);
+ g_free(html);
+
+ return TRUE;
+}
+
+void
+loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
+{
+ const char *uri = geturi(c);
+
+ switch (e) {
+ case WEBKIT_LOAD_STARTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ c->https = c->insecure = 0;
+ seturiparameters(c, uri, loadtransient);
+ if (c->errorpage)
+ c->errorpage = 0;
+ else
+ g_clear_object(&c->failedcert);
+ break;
+ case WEBKIT_LOAD_REDIRECTED:
+ setatom(c, AtomUri, uri);
+ c->title = uri;
+ seturiparameters(c, uri, loadtransient);
+ break;
+ case WEBKIT_LOAD_COMMITTED:
+ seturiparameters(c, uri, loadcommitted);
+ c->https = webkit_web_view_get_tls_info(c->view, &c->cert,
+ &c->tlserr);
+ break;
+ case WEBKIT_LOAD_FINISHED:
+ seturiparameters(c, uri, loadfinished);
+ /* Disabled until we write some WebKitWebExtension for
+ * manipulating the DOM directly.
+ evalscript(c, "document.documentElement.style.overflow = '%s'",
+ enablescrollbars ? "auto" : "hidden");
+ */
+ runscript(c);
+ break;
+ }
+ updatetitle(c);
+}
+
+void
+progresschanged(WebKitWebView *v, GParamSpec *ps, Client *c)
+{
+ c->progress = webkit_web_view_get_estimated_load_progress(c->view) *
+ 100;
+ updatetitle(c);
+}
+
+void
+titlechanged(WebKitWebView *view, GParamSpec *ps, Client *c)
+{
+ c->title = webkit_web_view_get_title(c->view);
+ updatetitle(c);
+}
+
+void
+mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers,
+ Client *c)
+{
+ WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h);
+
+ /* Keep the hit test to know where is the pointer on the next click */
+ c->mousepos = h;
+
+ if (hc & OnLink)
+ c->targeturi = webkit_hit_test_result_get_link_uri(h);
+ else if (hc & OnImg)
+ c->targeturi = webkit_hit_test_result_get_image_uri(h);
+ else if (hc & OnMedia)
+ c->targeturi = webkit_hit_test_result_get_media_uri(h);
+ else
+ c->targeturi = NULL;
+
+ c->overtitle = c->targeturi;
+ updatetitle(c);
+}
+
+gboolean
+permissionrequested(WebKitWebView *v, WebKitPermissionRequest *r, Client *c)
+{
+ ParamName param = ParameterLast;
+
+ if (WEBKIT_IS_GEOLOCATION_PERMISSION_REQUEST(r)) {
+ param = Geolocation;
+ } else if (WEBKIT_IS_USER_MEDIA_PERMISSION_REQUEST(r)) {
+ if (webkit_user_media_permission_is_for_audio_device(
+ WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r)))
+ param = AccessMicrophone;
+ else if (webkit_user_media_permission_is_for_video_device(
+ WEBKIT_USER_MEDIA_PERMISSION_REQUEST(r)))
+ param = AccessWebcam;
+ } else {
+ return FALSE;
+ }
+
+ if (curconfig[param].val.i)
+ webkit_permission_request_allow(r);
+ else
+ webkit_permission_request_deny(r);
+
+ return TRUE;
+}
+
+gboolean
+decidepolicy(WebKitWebView *v, WebKitPolicyDecision *d,
+ WebKitPolicyDecisionType dt, Client *c)
+{
+ switch (dt) {
+ case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
+ decidenavigation(d, c);
+ break;
+ case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
+ decidenewwindow(d, c);
+ break;
+ case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
+ decideresource(d, c);
+ break;
+ default:
+ webkit_policy_decision_ignore(d);
+ break;
+ }
+ return TRUE;
+}
+
+void
+decidenavigation(WebKitPolicyDecision *d, Client *c)
+{
+ WebKitNavigationAction *a =
+ webkit_navigation_policy_decision_get_navigation_action(
+ WEBKIT_NAVIGATION_POLICY_DECISION(d));
+
+ switch (webkit_navigation_action_get_navigation_type(a)) {
+ case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_RELOAD: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED: /* fallthrough */
+ case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
+ default:
+ /* Do not navigate to links with a "_blank" target (popup) */
+ if (webkit_navigation_policy_decision_get_frame_name(
+ WEBKIT_NAVIGATION_POLICY_DECISION(d))) {
+ webkit_policy_decision_ignore(d);
+ } else {
+ /* Filter out navigation to different domain ? */
+ /* get action→urirequest, copy and load in new window+view
+ * on Ctrl+Click ? */
+ webkit_policy_decision_use(d);
+ }
+ break;
+ }
+}
+