Security Policies for URI Schemes

We should use the new class WebKitSecurityManager to deal with security policies. We can access to a instance of this class using the WKWebContext webkit_web_context_get_security_manager method. We are able check or set the security level of certain URI scheme using this class methods.

Custom URI Schemes

WebKit2 API provide a easy way to register custom URI schemes. We can use the WKWebContext method webkit_web_context_register_uri_scheme. We should provide to this application a callback function where we handle the request.

This callback receives as parameter a instance of a WebKitURISchemeRequest that has information about the requested URI. In order to finish our request handling we should call webkit_uri_scheme_request_finish or webkit_uri_scheme_request_finish_error to load the page contents or an error page.

Here we have the example callback that the WebKit2 API documentation provides:

   1 static void
   2 about_uri_scheme_request_cb (WebKitURISchemeRequest *request,
   3                              gpointer                user_data)
   4 {
   5   GInputStream *stream;
   6   gsize         stream_length;
   7   const gchar  *path;
   8 
   9   path = webkit_uri_scheme_request_get_path (request);
  10   if (!g_strcmp0 (path, "plugins")) {
  11     /* Create a GInputStream with the contents of plugins about page, and
  12        set its length to stream_length */
  13   } else if (!g_strcmp0 (path, "memory")) {
  14     /* Create a GInputStream with the contents of memory about page, and
  15        set its length to stream_length */
  16   } else if (!g_strcmp0 (path, "applications")) {
  17     /* Create a GInputStream with the contents of applications about page,
  18        and set its length to stream_length */
  19   } else if (!g_strcmp0 (path, "example")) {
  20     gchar *contents;
  21 
  22     contents = g_strdup_printf ("<html><body><p>Example about page</p>"
  23                                 "</body></html>");
  24     stream_length = strlen (contents);
  25     stream = g_memory_input_stream_new_from_data (contents,
  26                                                   stream_length,
  27                                                   g_free);
  28   } else {
  29     GError *error;
  30 
  31     error = g_error_new (ABOUT_HANDLER_ERROR, ABOUT_HANDLER_ERROR_INVALID,
  32                          "Invalid about:%s page.", path);
  33     webkit_uri_scheme_request_finish_error (request, error);
  34     g_error_free (error);
  35     return;
  36   }
  37   webkit_uri_scheme_request_finish (request,
  38                                     stream,
  39                                     stream_length,
  40                                     "text/html");
  41   g_object_unref (stream);
  42 }

Permissions requests (Geolocalization for instance)

When we need to handle permissions requests we should connect to the WebView permission-request signal. The callback receives a WebKitPermissionRequest instance as parameter. It is an interface with the webkit_permission_request_allow and webkit_permission_request_deny methods. The API provides the WebKitGeolocationPermissionRequest class that implements the interface.

Here is the example that the documentation provides:

   1 static gboolean permission_request_cb (WebKitWebView *web_view,
   2                                        WebKitPermissionRequest *request,
   3                                        GtkWindow *parent_window)
   4 {
   5     GtkWidget *dialog = gtk_message_dialog_new (parent_window,
   6                                                 GTK_DIALOG_MODAL,
   7                                                 GTK_MESSAGE_QUESTION,
   8                                                 GTK_BUTTONS_YES_NO,
   9                                                 "Allow Permission Request?");
  10     gtk_widget_show (dialog);
  11     gint result = gtk_dialog_run (GTK_DIALOG (dialog));
  12 
  13     switch (result) {
  14     case GTK_RESPONSE_YES:
  15         webkit_permission_request_allow (request);
  16         break;
  17     default:
  18         webkit_permission_request_deny (request);
  19         break;
  20     }
  21     gtk_widget_destroy (dialog);
  22 
  23     return TRUE;
  24 }

If we need to handle the app behaviour when navigate to a new page, create a new window or starting the load of a resource we should connect the WebKitWebView decide-policy signal. The callback receives as parameter a WebKitPolicyDecisionType indicating what kind of policy decision we are handling and a WebKitPolicyDecision instance that allow us to indicate our decision (use, ignore or download).

To retrieve more specific information about the policy decision, we should downcast the WebKitPolicyDecision instance. In the following table we can find the match of WebKitPolicyDecisionTypes and the WebKitPolicyDecision downcast class:

Type

Downcast Class

NAVIGATION_ACTION

NavigationPolicyDecision

NEW_WINDOW_ACTION

NavigationPolicyDecision

RESPONSE

ResponsePolicyDecision

Searching text on a WebView

If we want to search some piece of text in a WebView we should get an instance of WebKitFindController. We can get one using the WebKitWebView webkit_web_view_get_find_controller method.

Apart from some getter methods we have a method to start a search (webkit_find_controller_search), to finish a search (webkit_find_controller_search_finish) and to go to the previous and next search.

We can specify to the search start method a mask of options with the values of WebKitFindOptions.

We had the parameters case_sensitive, forward and wrap when we used the WebKit1 API. They have been replaced by the next options:

Parameter (set to true)

Option

case_sensitive

do not set WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE

forward

do not set WEBKIT_FIND_OPTIONS_BACKWARDS

wrap

set WEBKIT_FIND_OPTIONS_WRAP_AROUND

Here we have an example about how we deal with searching contents on Yelp:

   1 /* Entry key-press signal handler.*/
   2 static gboolean
   3 find_entry_key_press (GtkEntry    *entry,
   4                       GdkEventKey *event,
   5                       YelpWindow  *window)
   6 {
   7     YelpWindowPrivate *priv = GET_PRIV (window);
   8     WebKitFindController *find_controller;
   9 
  10     find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (priv->view));
  11 
  12     if (event->keyval == GDK_KEY_Escape) {
  13         webkit_find_controller_search_finish (find_controller);
  14         return TRUE;
  15     }
  16 
  17     if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
  18         webkit_find_controller_search_next (find_controller);
  19         return TRUE;
  20     }
  21 
  22     return FALSE;
  23 }
  24 
  25 /* Button clicked signal handler */
  26 static void
  27 find_prev_clicked (GtkButton  *button,
  28                    YelpWindow *window)
  29 {
  30     YelpWindowPrivate *priv = GET_PRIV (window);
  31     WebKitFindController *find_controller;
  32 
  33     find_controller = webkit_web_view_get_find_controller (WEBKIT_WEB_VIEW (priv->view));
  34     webkit_find_controller_search_previous (find_controller);
  35 }

Context Menu

To edit the default menu we should connect the WebKitWebView context-menu. The callback function receives as parameter a instance of WebKitContextMenu that we can edit. In addition the boolean return value allow us to determine if this context menu is going to be displayed or not.

The WebKitContextMenu is a composite of WebKitContextMenuItems. These are defined using Gtk.Actions. The Gtk.Action class has a label property that is going to be used to create the context menu.

If we have a big amount of Actions, it's a good idea to use a GtkActionGroup to store these actions.

Disable Context Menu

If we want to disable the default context menu we just need to connect the context-menu signal to a function that just returns TRUE. The next code shows how to do it:

   1 static void
   2 my_view_init (MyView *view)
   3 {
   4     [...]
   5     g_signal_connect (view, "context-menu",
   6                       G_CALLBACK (view_context_menu), NULL);
   7     [...]
   8 }
   9 
  10 static gboolean
  11 view_context_menu (WebKitWebView       *web_view,
  12                    WebKitContextMenu   *context_menu,
  13                    GdkEvent            *event,
  14                    WebKitHitTestResult *hit_test_result,
  15                    gpointer             user_data)
  16 {
  17     return TRUE;
  18 }

Script Dialogs

When we need to handle a script dialog we should connect the WebView script-dialog signal. This signal receives a parameter a instance of WebKitScriptDialog. Using the function webkit_script_dialog_get_dialog_type we can get what type of script dialog we are handling:

ScriptDialogType

Other Methods

WEBKIT_SCRIPT_DIALOG_ALERT

WEBKIT_SCRIPT_DIALOG_CONFIRM

_confirm_set_confirmed

WEBKIT_SCRIPT_DIALOG_PROMPT

_prompt_get_default_text and _prompt_set_text

In addition we can get and set any information we need using the _get_message method for every type of dialog of the custom methods for confirm and prompt dialogs.

Here we have an example about how to deal with this on Yelp:

   1 static void
   2 yelp_view_init (YelpView *view)
   3 {
   4     [...]
   5     g_signal_connect (view, "script-dialog",
   6                       G_CALLBACK (view_script_dialog), NULL);
   7     [...]
   8 }
   9 
  10 static gboolean
  11 view_script_dialog (YelpView           *view,
  12                     WebKitScriptDialog *dialog,
  13                     gpointer            data)
  14 {
  15     WebKitScriptDialogType type = webkit_script_dialog_get_dialog_type (dialog);
  16 
  17     if (type != WEBKIT_SCRIPT_DIALOG_ALERT)
  18       return FALSE;
  19 
  20     printf ("\n\n===ALERT===\n%s\n\n", webkit_script_dialog_get_message (dialog));
  21     return TRUE;
  22 }

Printing

To be able to print we should get a instance of the class WebKitPrintOperation. The constructor of this class recieves as parameter the current WebView.

Once we have the instance we can set and get the page and print settings, run a dialog where the users sets their preferences (webkit_print_operation_run_dialog) or start printing (webkit_print_operation_print).

Here we have an example about how we deal with this on Yelp:

   1 static void
   2 view_print_action (GAction *action, GVariant *parameter, YelpView *view)
   3 {
   4     WebKitPrintOperation *print_operation;
   5     GtkWidget *window;
   6 
   7     window = gtk_widget_get_toplevel (GTK_WIDGET (view));
   8 
   9     print_operation = webkit_print_operation_new (WEBKIT_WEB_VIEW (view));
  10     webkit_print_operation_run_dialog (print_operation, GTK_WINDOW (window));
  11     g_object_unref (print_operation);
  12 }

Load Status

When we need to handle our WebView load status we should connect to the load-changed signal. This signal provides a parameter WKLoadEvent that indicates the new load status. The next table shows the match between the WebKit1 API signals to the WebKit2 API LoadEvents:

Old Signal

New WKLoadEvent

load-started

WEBKIT_LOAD_STARTED

load-committed

WEBKIT_LOAD_COMMITTED

load-finished

WEBKIT_LOAD_FINISHED

WEBKIT_LOAD_REDIRECTED

The example provided by the documentation is the following:

   1 static void web_view_load_changed (WebKitWebView  *web_view,
   2                                    WebKitLoadEvent load_event,
   3                                    gpointer        user_data)
   4 {
   5     switch (load_event) {
   6     case WEBKIT_LOAD_STARTED:
   7         /* New load, we have now a provisional URI */
   8         provisional_uri = webkit_web_view_get_uri (web_view);
   9         /* Here we could start a spinner or update the
  10          * location bar with the provisional URI */
  11         break;
  12     case WEBKIT_LOAD_REDIRECTED:
  13         redirected_uri = webkit_web_view_get_uri (web_view);
  14         break;
  15     case WEBKIT_LOAD_COMMITTED:
  16         /* The load is being performed. Current URI is
  17          * the final one and it won't change unless a new
  18          * load is requested or a navigation within the
  19          * same page is performed */
  20         uri = webkit_web_view_get_uri (web_view);
  21         break;
  22     case WEBKIT_LOAD_FINISHED:
  23         /* Load finished, we can now stop the spinner */
  24         break;
  25     }
  26 }

Handling Plugins

To be able to load certain web contents such as Java applets or Flash scripts we need plugins. We have a class named WebKitPlugin that stores information about each plugin. If we need to get a list of available plugins we should call the asyncronous method webkit_web_context_get_plugins and when the callback function were executed, use the webkit_web_context_get_plugins_finish method to get a list of WebKitPlugins.

To set a certain directory to load plugins we should use the WKWebContext webkit_web_context_set_additional_plugins_directory method.

The next code show how Epithany creates a list of plugins page using the previous funcions and custom URI requests (the full code can be found here):

   1 static void
   2 get_plugins_cb (WebKitWebContext *web_context,
   3                 GAsyncResult *result,
   4                 EphyAboutRequest *about_request)
   5 {
   6   GList *plugin_list, *p;
   7 
   8   [...]
   9 
  10   plugin_list = webkit_web_context_get_plugins_finish (web_context, result, NULL);
  11   for (p = plugin_list; p; p = p->next) {
  12     WebKitPlugin *plugin = WEBKIT_PLUGIN (p->data);
  13     GList *m, *mime_types;
  14 
  15     g_string_append_printf (data_str, [...],
  16                             webkit_plugin_get_name (plugin),
  17                             webkit_plugin_get_description (plugin));
  18 
  19     mime_types = webkit_plugin_get_mime_info_list (plugin);
  20 
  21     for (m = mime_types; m; m = m->next) {
  22       WebKitMimeInfo *mime_info = (WebKitMimeInfo *) m->data;
  23       const gchar * const *extensions;
  24       guint i;
  25 
  26       g_string_append_printf (data_str, "<tr><td>%s</td><td>%s</td><td>",
  27                               webkit_mime_info_get_mime_type (mime_info),
  28                               webkit_mime_info_get_description (mime_info));
  29 
  30       extensions = webkit_mime_info_get_extensions (mime_info);
  31       for (i = 0; extensions && extensions[i] != NULL; i++)
  32         g_string_append_printf (data_str, "%s%c", extensions[i],
  33                                 extensions[i + 1] ? ',' : ' ');
  34       [...]
  35     }
  36   }
  37 
  38   g_list_free_full (plugin_list, g_object_unref);
  39 
  40   data_length = data_str->len;
  41   ephy_about_handler_finish_request (about_request->request, g_string_free (data_str, FALSE), data_length);
  42   ephy_about_request_free (about_request);
  43 }
  44 
  45 static gboolean
  46 ephy_about_handler_handle_plugins (EphyAboutHandler *handler,
  47                                    WebKitURISchemeRequest *request)
  48 {
  49   webkit_web_context_get_plugins (webkit_web_context_get_default (),
  50                                   NULL,
  51                                   (GAsyncReadyCallback)get_plugins_cb,
  52                                   ephy_about_request_new (handler, request));
  53   return TRUE;
  54 }

Cookies

If we need to work with cookies in our application we should use the WebKitCookieManager class. To obtain an instance of this class we should call the WKWebContext webkit_web_context_get_cookie_manager method. Using this class we can change the cookie accept policy, we can get a list of domains which contain cookies and we can delete cookies.

Here you can find an example of how Epiphany creates a dialog to view and/or delete the existent cookies.

Authentication

If we need to handle HTTP authentication we should connect the WKWebView authenticate signal. The callback has a WebKitAuthenticationRequest instance as parameter that would provide all the necesary info. We should return TRUE if we want to stop other handler from being invoked or FALSE if we want to propagate the event.

Spell Checking

To deal with spell checking we should use the WebKitWebContext class. We have methods to get and set the spell checking status (enabled or disabled) and to get and set the list of languages that WebKit is going to check.

Executing JavaScript

To execute Javascript code in our view we should have the WebKitWebSettings enable-javascript property set to true. We should use the WebView run_javascript or the run_javascript_from_gresource methods.

Both method have a callback function as parameter. This function will be executed when the script execution ends. We can call run_javascript_finish or run_javascript_from_gresource_finish methods (depending if we are using a string or a gresource) to get the scripts execution result.

We can find an example of how to use this functions in the WebKit2 API documentation:

   1 static void
   2 web_view_javascript_finished (GObject      *object,
   3                               GAsyncResult *result,
   4                               gpointer      user_data)
   5 {
   6     WebKitJavascriptResult *js_result;
   7     JSValueRef              value;
   8     JSGlobalContextRef      context;
   9     GError                 *error = NULL;
  10 
  11     js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
  12     if (!js_result) {
  13         g_warning ("Error running javascript: %s", error->message);
  14         g_error_free (error);
  15         return;
  16     }
  17 
  18     context = webkit_javascript_result_get_global_context (js_result);
  19     value = webkit_javascript_result_get_value (js_result);
  20     if (JSValueIsString (context, value)) {
  21         JSStringRef js_str_value;
  22         gchar      *str_value;
  23         gsize       str_length;
  24 
  25         js_str_value = JSValueToStringCopy (context, value, NULL);
  26         str_length = JSStringGetMaximumUTF8CStringSize (js_str_value);
  27         str_value = (gchar *)g_malloc (str_length);
  28         JSStringGetUTF8CString (js_str_value, str_value, str_length);
  29         JSStringRelease (js_str_value);
  30         g_print ("Script result: %s\n", str_value);
  31         g_free (str_value);
  32     } else {
  33         g_warning ("Error running javascript: unexpected return value");
  34     }
  35     webkit_javascript_result_unref (js_result);
  36 }
  37 
  38 static void
  39 web_view_get_link_url (WebKitWebView *web_view,
  40                        const gchar   *link_id)
  41 {
  42     gchar *script;
  43 
  44     script = g_strdup_printf ("window.document.getElementById('%s').href;", link_id);
  45     webkit_web_view_run_javascript (web_view, script, NULL, web_view_javascript_finished, NULL);
  46     g_free (script);
  47 }

Scrolling

The WebKit2 WebView widget has its own scrollbars so we don't need to include it inside a scrollable window. If we need to scroll our view we can user Javascript and call the method webkit_web_view_run_javascript with a code similar to the following one:

window.scrollTo(500,0);

Then our view will scroll.

Dealing with DOM Tree

WebKit2 API has a different architecture than WK1 so DOM tree cannot be processed in the same process than our WebView has its logic. To fix this we two choices:

  • Use Javascript: The DOM is manipulated by injected Javascript.

  • Using a Web Extension: Uses the new WebExtension API to load a .so file into the web process to do the DOM manipulation.

    • We can user native methods such as context-menu (new in WK 2.7.2) signal in WebKitWebPage.

    • We can communicate our extension with the UI process using a custom DBus API.

We have examples about how to implement Javascript and DBus communication this repository. Examples are written in Vala programming language.

In addition Carlos GarcĂ­a Campos has created a tutorial about how to create new web extensions.


Previous Top Next

Projects/WebKitGtk/ProgrammingGuide/Cookbook (last edited 2014-11-20 11:05:34 by MarcosChavarria)