Commit 1d7e3e75 authored by Milan Crha's avatar Milan Crha

EWebView: Use JavaScriptCore API of WebKitGTK+

All the previews (in Mail, Contacts, Memos and Tasks) stop using
deprecated WebKitGTK+ DOM API through a WebExtension (and its D-Bus
API) and start using the JavaScriptCore API (and JavaScript as such).
This allows to lightweight the WebExtension for the EWebView
significantly, including drop of the D-Bus API of it, fully relying
on the Inter-Process-Communication of WebKitGTK+ itself.
parent cac20e41
......@@ -82,7 +82,7 @@ set(gsettings_desktop_schemas_minimum_version 2.91.92)
set(libpst_minimum_version 0.6.54)
set(libxml_minimum_version 2.7.3)
set(shared_mime_info_minimum_version 0.22)
set(webkit2gtk_minimum_version 2.16.0)
set(webkit2gtk_minimum_version 2.24.0)
# Optional Packages
set(champlain_minimum_version 0.12)
......@@ -178,6 +178,7 @@ set(plugindir "${privlibdir}/plugins")
set(soundsdir "${privdatadir}/sounds")
set(uidir "${privdatadir}/ui")
set(viewsdir "${privdatadir}/views")
set(webkitdatadir "${privdatadir}/webkit")
set(webextensionsdir "${privlibdir}/web-extensions")
set(webextensionswebkiteditordir "${webextensionsdir}/webkit-editor")
......
set(filedeps)
set(desktopdir ${SHARE_INSTALL_PREFIX}/applications)
set(themedir ${privdatadir}/theme)
configure_file(org.gnome.Evolution.desktop.in.in
org.gnome.Evolution.desktop.in
......@@ -30,11 +29,6 @@ configure_file(org.gnome.Evolution.appdata.xml.in.in
add_appdata_file(${CMAKE_CURRENT_BINARY_DIR}/org.gnome.Evolution.appdata.xml.in org.gnome.Evolution.appdata.xml)
install(FILES webview.css
webview-print.css
DESTINATION ${themedir}
)
set(SCHEMAS
org.gnome.evolution.gschema.xml
org.gnome.evolution.addressbook.gschema.xml
......@@ -83,3 +77,4 @@ add_subdirectory(images)
add_subdirectory(sounds)
add_subdirectory(ui)
add_subdirectory(views)
add_subdirectory(webkit)
set(DATA_FILES
e-convert.js
e-web-view.js
webview.css
webview-print.css
)
install(FILES ${DATA_FILES}
DESTINATION ${webkitdatadir}
)
'use strict';
/* semi-convention: private functions start with lower-case letter,
public functions start with upper-case letter. */
function EvoConvertToPlainText(element)
{
if (!element)
return null;
return element.innerText;
}
This diff is collapsed.
......@@ -385,26 +385,14 @@ contact_display_link_clicked (EWebView *web_view,
}
static void
contact_display_notify_web_extension_proxy_cb (GObject *web_view,
GParamSpec *param,
gpointer user_data)
contact_display_content_loaded_cb (EWebView *web_view,
const gchar *iframe_id,
gpointer user_data)
{
GDBusProxy *web_extension;
GVariant* result;
web_extension = e_web_view_get_web_extension_proxy (E_WEB_VIEW (web_view));
if (web_extension) {
result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
web_extension,
"EABContactFormatterBindDOM",
g_variant_new (
"(t)",
webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (web_view))),
NULL);
if (result)
g_variant_unref (result);
}
g_return_if_fail (EAB_IS_CONTACT_DISPLAY (web_view));
e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (web_view), e_web_view_get_cancellable (web_view),
"Evo.VCardBind(%s);", iframe_id);
}
static void
......@@ -532,8 +520,8 @@ eab_contact_display_init (EABContactDisplay *display)
G_CALLBACK (contact_display_web_process_crashed_cb), NULL);
g_signal_connect (
web_view, "notify::web-extension-proxy",
G_CALLBACK (contact_display_notify_web_extension_proxy_cb), NULL);
web_view, "content-loaded",
G_CALLBACK (contact_display_content_loaded_cb), NULL);
g_signal_connect (
web_view, "style-updated",
G_CALLBACK (load_contact), NULL);
......
......@@ -51,7 +51,7 @@
#define HTML_HEADER "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n" \
"<head>\n<meta name=\"generator\" content=\"Evolution Addressbook Component\">\n" \
"<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\">" \
"<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://$EVOLUTION_WEBKITDATADIR/webview.css\">" \
"<style type=\"text/css\">\n" \
" div#header { width:100%; clear: both; }\n" \
" div#columns { width: 100%; clear: both; }\n" \
......@@ -621,7 +621,7 @@ render_contact_list_row (EABContactFormatter *formatter,
g_string_append_printf (
buffer,
"<td width=" IMAGE_COL_WIDTH " valign=\"top\" align=\"left\">"
"<button type=\"button\" id=\"%s\" class=\"header-collapse _evo_collapse_button\" style=\"display: inline-block;\">"
"<button type=\"button\" id=\"%s\" class=\"header-collapse _evo_vcard_collapse_button\" style=\"display: inline-block;\">"
"<img src=\"gtk-stock://pan-down-symbolic\" />"
"</button>"
"</td><td width=\"100%%\" align=\"left\">%s",
......
......@@ -185,7 +185,6 @@ add_dependencies(evolution-calendar
target_compile_definitions(evolution-calendar PRIVATE
-DG_LOG_DOMAIN=\"evolution-calendar\"
-DEVOLUTION_ETSPECDIR=\"${etspecdir}\"
-DEVOLUTION_PRIVDATADIR=\"${privdatadir}\"
)
target_compile_options(evolution-calendar PUBLIC
......
......@@ -61,7 +61,7 @@ struct _ECalComponentPreviewPrivate {
#define HTML_HEADER "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n" \
"<head>\n<meta name=\"generator\" content=\"Evolution Calendar Component\">\n" \
"<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\">\n" \
"<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://$EVOLUTION_WEBKITDATADIR/webview.css\">\n" \
"<style>\n" \
".description { font-family: monospace; font-size: 1em; }\n" \
"</style>\n" \
......
......@@ -270,8 +270,9 @@ set(SOURCES
e-util-private.h
e-webdav-browser.c
e-web-extension-container.c
e-web-view-preview.c
e-web-view.c
e-web-view-jsc-utils.c
e-web-view-preview.c
e-widget-undo.c
e-xml-utils.c
ea-calendar-cell.c
......@@ -543,8 +544,9 @@ set(HEADERS
e-util-enums.h
e-webdav-browser.h
e-web-extension-container.h
e-web-view-preview.h
e-web-view.h
e-web-view-jsc-utils.h
e-web-view-preview.h
e-widget-undo.h
e-xml-utils.h
ea-calendar-cell.h
......@@ -613,6 +615,7 @@ target_compile_definitions(evolution-util PRIVATE
-DEVOLUTION_UIDIR=\"${uidir}\"
-DEVOLUTION_RULEDIR=\"${privdatadir}\"
-DEVOLUTION_WEB_EXTENSIONS_DIR=\"${webextensionsdir}\"
-DEVOLUTION_WEBKITDATADIR=\"${webkitdatadir}\"
-DEVOLUTION_TESTGIOMODULESDIR=\"${CMAKE_CURRENT_BINARY_DIR}\"
-DEVOLUTION_TESTTOPSRCDIR=\"${CMAKE_SOURCE_DIR}\"
-DLIBEUTIL_COMPILATION
......@@ -808,6 +811,7 @@ add_private_programs_simple(
test-source-config
test-source-selector
test-tree-view-frame
test-web-view-jsc
)
add_private_program(test-html-editor-units
......
......@@ -59,6 +59,7 @@ e_file_request_process_sync (EContentRequest *request,
GFileInputStream *file_input_stream;
GFileInfo *info;
goffset total_size;
gchar *filename = NULL;
SoupURI *suri;
g_return_val_if_fail (E_IS_FILE_REQUEST (request), FALSE);
......@@ -70,7 +71,13 @@ e_file_request_process_sync (EContentRequest *request,
suri = soup_uri_new (uri);
g_return_val_if_fail (suri != NULL, FALSE);
file = g_file_new_for_path (suri->path);
if (g_strcmp0 (suri->host, "$EVOLUTION_WEBKITDATADIR") == 0) {
filename = g_build_filename (EVOLUTION_WEBKITDATADIR, suri->path, NULL);
} else if (g_strcmp0 (suri->host, "$EVOLUTION_IMAGESDIR") == 0) {
filename = g_build_filename (EVOLUTION_IMAGESDIR, suri->path, NULL);
}
file = g_file_new_for_path (filename ? filename : suri->path);
file_input_stream = g_file_read (file, cancellable, error);
if (file_input_stream) {
......@@ -97,7 +104,7 @@ e_file_request_process_sync (EContentRequest *request,
if (file_input_stream) {
*out_stream = G_INPUT_STREAM (file_input_stream);
*out_stream_length = (gint64) total_size;
*out_mime_type = g_content_type_guess (suri->path, NULL, 0, NULL);
*out_mime_type = g_content_type_guess (filename ? filename : suri->path, NULL, 0, NULL);
} else {
*out_stream = NULL;
*out_stream_length = (gint64) total_size;
......@@ -106,6 +113,7 @@ e_file_request_process_sync (EContentRequest *request,
g_object_unref (file);
soup_uri_free (suri);
g_free (filename);
return file_input_stream != NULL;
}
......
......@@ -56,6 +56,7 @@ const gchar *_e_get_sounddir (void) G_GNUC_CONST;
const gchar *_e_get_sysconfdir (void) G_GNUC_CONST;
const gchar *_e_get_toolsdir (void) G_GNUC_CONST;
const gchar *_e_get_uidir (void) G_GNUC_CONST;
const gchar *_e_get_webkitdatadir (void) G_GNUC_CONST;
#undef DATADIR
#define DATADIR _e_get_datadir ()
......@@ -123,6 +124,9 @@ const gchar *_e_get_uidir (void) G_GNUC_CONST;
#undef EVOLUTION_RULEDIR
#define EVOLUTION_RULEDIR _e_get_ruledir ()
#undef EVOLUTION_WEBKITDATADIR
#define EVOLUTION_WEBKITDATADIR _e_get_webkitdatadir ()
#endif /* G_OS_WIN32 */
#endif /* _E_UTIL_PRIVATE_H_ */
......@@ -266,8 +266,9 @@
#include <e-util/e-webdav-browser.h>
#include <e-util/e-web-extension-container.h>
#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
#include <e-util/e-web-view-preview.h>
#include <e-util/e-web-view.h>
#include <e-util/e-web-view-jsc-utils.h>
#include <e-util/e-web-view-preview.h>
#endif
#include <e-util/e-widget-undo.h>
#include <e-util/e-xml-utils.h>
......
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2019 Red Hat (www.redhat.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "evolution-config.h"
#include <webkit2/webkit2.h>
#include "e-web-view-jsc-utils.h"
gboolean
e_web_view_jsc_get_object_property_boolean (JSCValue *jsc_object,
const gchar *property_name,
gboolean default_value)
{
JSCValue *value;
gboolean res = default_value;
g_return_val_if_fail (JSC_IS_VALUE (jsc_object), default_value);
g_return_val_if_fail (property_name != NULL, default_value);
value = jsc_value_object_get_property (jsc_object, property_name);
if (!value)
return default_value;
if (jsc_value_is_boolean (value))
res = jsc_value_to_boolean (value);
g_clear_object (&value);
return res;
}
gint32
e_web_view_jsc_get_object_property_int32 (JSCValue *jsc_object,
const gchar *property_name,
gint32 default_value)
{
JSCValue *value;
gint32 res = default_value;
g_return_val_if_fail (JSC_IS_VALUE (jsc_object), default_value);
g_return_val_if_fail (property_name != NULL, default_value);
value = jsc_value_object_get_property (jsc_object, property_name);
if (!value)
return default_value;
if (jsc_value_is_number (value))
res = jsc_value_to_int32 (value);
g_clear_object (&value);
return res;
}
gdouble
e_web_view_jsc_get_object_property_double (JSCValue *jsc_object,
const gchar *property_name,
gdouble default_value)
{
JSCValue *value;
gdouble res = default_value;
g_return_val_if_fail (JSC_IS_VALUE (jsc_object), default_value);
g_return_val_if_fail (property_name != NULL, default_value);
value = jsc_value_object_get_property (jsc_object, property_name);
if (!value)
return default_value;
if (jsc_value_is_number (value))
res = jsc_value_to_double (value);
g_clear_object (&value);
return res;
}
gchar *
e_web_view_jsc_get_object_property_string (JSCValue *jsc_object,
const gchar *property_name,
const gchar *default_value)
{
JSCValue *value;
gchar *res;
g_return_val_if_fail (JSC_IS_VALUE (jsc_object), NULL);
g_return_val_if_fail (property_name != NULL, NULL);
value = jsc_value_object_get_property (jsc_object, property_name);
if (!value)
return g_strdup (default_value);
if (jsc_value_is_string (value))
res = jsc_value_to_string (value);
else
res = g_strdup (default_value);
g_clear_object (&value);
return res;
}
gchar *
e_web_view_jsc_printf_script (const gchar *script_format,
...)
{
gchar *script;
va_list va;
g_return_val_if_fail (script_format != NULL, NULL);
va_start (va, script_format);
script = e_web_view_jsc_vprintf_script (script_format, va);
va_end (va);
return script;
}
gchar *
e_web_view_jsc_vprintf_script (const gchar *script_format,
va_list va)
{
GString *script;
g_return_val_if_fail (script_format != NULL, NULL);
script = g_string_sized_new (128);
e_web_view_jsc_vprintf_script_gstring (script, script_format, va);
return g_string_free (script, FALSE);
}
void
e_web_view_jsc_printf_script_gstring (GString *script,
const gchar *script_format,
...)
{
va_list va;
g_return_if_fail (script != NULL);
g_return_if_fail (script_format != NULL);
va_start (va, script_format);
e_web_view_jsc_vprintf_script_gstring (script, script_format, va);
va_end (va);
}
void
e_web_view_jsc_vprintf_script_gstring (GString *script,
const gchar *script_format,
va_list va)
{
const gchar *ptr;
g_return_if_fail (script != NULL);
g_return_if_fail (script_format != NULL);
if (script->len)
g_string_append_c (script, '\n');
for (ptr = script_format; *ptr; ptr++) {
if (*ptr == '\\') {
g_warn_if_fail (ptr[1]);
g_string_append_c (script, ptr[0]);
g_string_append_c (script, ptr[1]);
ptr++;
} else if (*ptr == '%') {
g_warn_if_fail (ptr[1]);
switch (ptr[1]) {
case '%':
g_string_append_c (script, ptr[1]);
break;
/* Using %x for boolean, because %b is unknown to gcc, thus it claims format warnings */
case 'x': {
gboolean arg = va_arg (va, gboolean);
g_string_append (script, arg ? "true" : "false");
} break;
case 'd': {
gint arg = va_arg (va, gint);
g_string_append_printf (script, "%d", arg);
} break;
case 'f': {
gdouble arg = va_arg (va, gdouble);
g_string_append_printf (script, "%f", arg);
} break;
case 's': {
const gchar *arg = va_arg (va, const gchar *);
/* Enclose strings into double-quotes */
g_string_append_c (script, '\"');
/* Escape significant characters */
if (arg && (strchr (arg, '\"') ||
strchr (arg, '\\') ||
strchr (arg, '\n') ||
strchr (arg, '\r') ||
strchr (arg, '\t'))) {
const gchar *ptr2;
for (ptr2 = arg; *ptr2; ptr2++) {
if (*ptr2 == '\\')
g_string_append (script, "\\\\");
else if (*ptr2 == '\"')
g_string_append (script, "\\\"");
else if (*ptr2 == '\r')
g_string_append (script, "\\r");
else if (*ptr2 == '\n')
g_string_append (script, "\\n");
else if (*ptr2 == '\t')
g_string_append (script, "\\t");
else
g_string_append_c (script, *ptr2);
}
} else if (arg && *arg) {
g_string_append (script, arg);
}
g_string_append_c (script, '\"');
} break;
default:
g_warning ("%s: Unknown percent tag '%c'", G_STRFUNC, *ptr);
break;
}
ptr++;
} else {
g_string_append_c (script, *ptr);
}
}
}
static void
ewv_jsc_call_done_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
WebKitJavascriptResult *js_result;
gchar *script = user_data;
GError *error = NULL;
js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source), result, &error);
if (error) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
(!g_error_matches (error, WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED) ||
/* WebKit can return empty error message, thus ignore those. */
(error->message && *(error->message))))
g_warning ("Failed to call '%s' function: %s:%d: %s", script, g_quark_to_string (error->domain), error->code, error->message);
g_clear_error (&error);
}
if (js_result) {
JSCException *exception;
JSCValue *value;
value = webkit_javascript_result_get_js_value (js_result);
exception = jsc_context_get_exception (jsc_value_get_context (value));
if (exception)
g_warning ("Failed to call '%s': %s", script, jsc_exception_get_message (exception));
webkit_javascript_result_unref (js_result);
}
g_free (script);
}
void
e_web_view_jsc_run_script (WebKitWebView *web_view,
GCancellable *cancellable,
const gchar *script_format,
...)
{
gchar *script;
va_list va;
g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (script_format != NULL);
va_start (va, script_format);
script = e_web_view_jsc_vprintf_script (script_format, va);
va_end (va);
e_web_view_jsc_run_script_take (web_view, script, cancellable);
}
/* Assumes ownership of the 'script' variable and frees is with g_free(), when no longe needed. */
void
e_web_view_jsc_run_script_take (WebKitWebView *web_view,
gchar *script,
GCancellable *cancellable)
{
g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (script != NULL);
webkit_web_view_run_javascript (web_view, script, cancellable, ewv_jsc_call_done_cb, script);
}
void
e_web_view_jsc_set_element_hidden (WebKitWebView *web_view,
const gchar *iframe_id,
const gchar *element_id,
gboolean value,
GCancellable *cancellable)
{
g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (element_id != NULL);
e_web_view_jsc_run_script (web_view, cancellable,
"Evo.SetElementHidden(%s,%s,%d)",
iframe_id,
element_id,
value ? 1 : 0);
}
void
e_web_view_jsc_set_element_disabled (WebKitWebView *web_view,
const gchar *iframe_id,
const gchar *element_id,
gboolean value,
GCancellable *cancellable)
{
g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (element_id != NULL);
e_web_view_jsc_run_script (web_view, cancellable,
"Evo.SetElementDisabled(%s,%s,%d)",
iframe_id,
element_id,
value ? 1 : 0);
}
void
e_web_view_jsc_set_element_checked (WebKitWebView *web_view,
const gchar *iframe_id,
const gchar *element_id,
gboolean value,
GCancellable *cancellable)
{
g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (element_id != NULL);
e_web_view_jsc_run_script (web_view, cancellable,
"Evo.SetElementChecked(%s,%s,%d)",
iframe_id,
element_id,
value ? 1 : 0);
}
void
e_web_view_jsc_set_element_style_property (WebKitWebView *web_view,
const gchar *iframe_id,
const gchar *element_id,
const gchar *property_name,
const gchar *value,
GCancellable *cancellable)
{
g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (element_id != NULL);
g_return_if_fail (property_name != NULL);
e_web_view_jsc_run_script (web_view, cancellable,
"Evo.SetElementStyleProperty(%s,%s,%s,%s)",
iframe_id,
element_id,
property_name,
value);
}
void
e_web_view_jsc_set_element_attribute (WebKitWebView *web_view,
const gchar *iframe_id,
const gchar *element_id,
const gchar *namespace_uri,
const gchar *qualified_name,
const gchar *value,
GCancellable *cancellable)
{
g_return_if_fail (WEBKIT_IS_WEB_VIEW (web_view));
g_return_if_fail (element_id != NULL);
g_return_if_fail (qualified_name != NULL);
e_web_view_jsc_run_script (web_view, cancellable,
"Evo.SetElementAttribute(%s,%s,%s,%s,%s)",
iframe_id,
element_id,
namespace_uri,