我们使用GTK+开发的时候,为了方便地显示Web内容,可以把一个webkit 内嵌进去。相应的GTK+的接口实现为webkit2gtk。
使用方法
使用方法非常简单,只要使用webkit_web_view_new来生成一个WebKitWebView,之后当成其它的普通的GtkWidget放入Gtk的布局中就行了。
如:
GtkWidget *box = mVbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
GtkWidget *view = webkit_web_view_new_with_context (context);
gtk_box_pack_start(GTK_BOX(box), view, TRUE, TRUE, 0);
之后,就可以使用webkit_web_view_load_uri函数加载网页了。
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), "https://www.baidu.com/");
自定义协议思路
但是,一般我们开发过程中,经常需要自定义Web的请求协议,而不是常规地HTTP,以实现我们自定义的任务。比如,要加载的文件在本地,或者在数据库中,等等。
这时候,我们就可以先生成一个WebKitWebContext,之后再生成这个Context之上的WebKitWebView,然后使用WebKitWebContext注册协议。
WebKitWebContext的注册协议函数原型为:
void
webkit_web_context_register_uri_scheme
(WebKitWebContext *context,
const gchar *scheme,
WebKitURISchemeRequestCallback callback,
gpointer user_data,
GDestroyNotify user_data_destroy_func);
其中,scheme为协议名称,callback为加载这种协议的时候执行的回调函数,user_data为回调时带的上层参数。
而WebKitURISchemeRequestCallback的定义为:
void
(*WebKitURISchemeRequestCallback) (WebKitURISchemeRequest *request,
gpointer user_data);
其中,user_data就是使用webkit_web_context_register_uri_scheme注册的时候的user_data参数。而WebKitURISchemeRequest则是我们需要重点关注以及处理的结构,我们就是要通过它来实现自定义的加载行为。
WebKitURISchemeRequest支持的接口为:
const gchar * webkit_uri_scheme_request_get_scheme ()
const gchar * webkit_uri_scheme_request_get_uri ()
const gchar * webkit_uri_scheme_request_get_path ()
WebKitWebView * webkit_uri_scheme_request_get_web_view ()
void webkit_uri_scheme_request_finish ()
void webkit_uri_scheme_request_finish_error ()
在我们注册的回调函数里面,就可以通过webkit_uri_scheme_request_get_uri或者webkit_uri_scheme_request_get_path来获取页面正要请求的资源,我们为它准备好相应资源以后,再通过webkit_uri_scheme_request_finish来返回给WebKitWebView,或者使用webkit_uri_scheme_request_finish_error通知它失败。
其中,webkit_uri_scheme_request_finish还有webkit_uri_scheme_request_finish_error的原型分别为:
void
webkit_uri_scheme_request_finish (WebKitURISchemeRequest *request,
GInputStream *stream,
gint64 stream_length,
const gchar *mime_type);
void
webkit_uri_scheme_request_finish_error
(WebKitURISchemeRequest *request,
GError *error);
看到这里,相信我们对于整个逻辑就比较清楚了。
自定义协议举例
比如,我们要增加一种协议叫做helloworld,我们希望WebKitWebView访问本地文件的格式为helloworld:///home/user1/p.html,就可以如下实现:
WebKitWebContext *context = webkit_web_context_new_ephemeral ();
GtkWidget *view = webkit_web_view_new_with_context (context);
webkit_web_context_register_uri_scheme (
context, "helloworld",
WebKitURISchemeRequestCallback (webcontext_load_helloword_callback), NULL,
NULL);
……
void
webcontext_load_helloworld_callback (WebKitURISchemeRequest *request,
gpointer arg1)
{
gssize stream_length;
const gchar *path, *type;
path = webkit_uri_scheme_request_get_path (request);
type = "text/html";
gchar *contents = /* 本地资源读取过程略 */;
if (contents)
{
/* 特别注意:这个实现中,contents 必须是需要free的内存,因为stream被处理完成以后,会在ref为0的时候自动释放,这时候会一起释放contents,释放的函数为g_memory_input_stream_new_from_data的最后一个回调函数,这里设为free。*/
GInputStream *stream = g_memory_input_stream_new_from_data (
contents, stream_length, free);
webkit_uri_scheme_request_finish (request, stream, stream_length, type);
g_object_unref (stream);
}
else
{
GError *error
= g_error_new (G_FILE_ERROR_FAULT, -1, "Not found path:%s page.", path);
webkit_uri_scheme_request_finish_error (request, error);
g_error_free (error);
}
}
页面联动(判断滚动到页尾举例)
WebKitWebView的页面进行滚动,有的是用户滑动的滚动条,也有的是页面里面的Javascript脚本操作,但是这些滚动的状态,如何通知到我们的代码调用这里呢?
这就需要WebKitWebView关联的WebKitUserContentManager。
通过名字也可以看出来,这个结构的目的,就是Web页面内容的管理器。通过这个管理器,我们就可以把页面的状态与我们的代码联系起来。
使用方法为,在WebKitWebView的WebKitUserContentManager里,注册相应的Javascript消息,然后把消息与WebKitUserContentManager的回调函数连接。
之后,在页面里面执行JavaScript代码,使用window.webkit.messageHandlers发送相应的消息到我们的WebKitUserContentManager,就触发了我们的回调函数。
比如,我们要判断Web页面是否到达末尾,就可以这样实现:
首先,在UserContentManager里注册一个消息,然后把这个消息与回调函数连接:
WebKitUserContentManager *man
= webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (view));
webkit_user_content_manager_register_script_message_handler (man, "bottom");
g_signal_connect (man, "script-message-received::bottom",
G_CALLBACK (webview_arrive_bottom), this);
……
void
webview_arrive_bottom (WebKitUserContentManager *man,
WebKitJavascriptResult *res, gpointer arg1)
{
printf ("webkit arrived bottom \n");
}
之后,在WebKitWebView里,判断到达内容末尾的时候,使用Javascript发送"bottom"消息:
const gchar *javascript
= "if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { window.webkit.messageHandlers.bottom.postMessage('reached');}";
webkit_web_view_run_javascript (view, javascript, NULL, NULL, NULL);