diff --git a/bindings/meson.build b/bindings/meson.build new file mode 100644 index 00000000..6e9c7256 --- /dev/null +++ b/bindings/meson.build @@ -0,0 +1,70 @@ +# Custom VAPIs +# NOTE: We can't directly use the dependencies returned by dependency() for +# webkit2gtk (and the web extensions), since Meson then adds the packages from +# the system directories. Since there might be conflicts with our custom VAPI's, +# we need to glue pieces together. We satisfy GCC by looking up the relevant +# include directories and we please the linker by using cc.find_library() + + +girdir = gobject_introspection.get_pkgconfig_variable('girdir') +webkit2gtk_include = include_directories( + webkit2gtk_dep.get_pkgconfig_variable('includedir') + '/webkitgtk-4.0' +) + +javascriptcoregtk = declare_dependency( + dependencies: [ + javascriptcoregtk_lib, + javascriptcoregtk_vapi + ], + include_directories: webkit2gtk_include +) + +webkit2gtk_vapi = gnome.generate_vapi('webkit2gtk-4.8', + sources: join_paths(girdir, 'WebKit2-4.0.gir'), + vapi_dirs: vapi_dir, + metadata_dirs: metadata_dir, + packages: [ + 'gtk+-3.0', + 'libsoup-2.4', + 'javascriptcore-4.0', + ], +) + +webkit2gtk = declare_dependency( + dependencies: [ + cc.find_library('webkit2gtk-4.0'), + glib, + gtk, + javascriptcoregtk, + libsoup, + webkit2gtk_vapi + ], + include_directories: webkit2gtk_include +) + + +webkit2gtk_web_extension_vapi = gnome.generate_vapi('webkit2gtk-web-extension-4.0', + sources: [ + join_paths(girdir, 'WebKit2WebExtension-4.0.gir'), + join_paths('metadata', 'WebKit2WebExtension-4.0-custom.vala'), + ], + vapi_dirs: [vapi_dir, meson.current_build_dir()], + metadata_dirs: metadata_dir, + packages: [ + 'gtk+-3.0', + 'libsoup-2.4', + 'javascriptcore-4.0', + ], +) + +webkit2gtk_web_extension = declare_dependency( + dependencies: [ + cc.find_library('webkit2gtk-4.0'), + glib, + gtk, + javascriptcoregtk, + libsoup, + webkit2gtk_web_extension_vapi, + ], + include_directories: webkit2gtk_include +) diff --git a/bindings/metadata/Soup-2.4.metadata b/bindings/metadata/Soup-2.4.metadata new file mode 100644 index 00000000..f3e72e81 --- /dev/null +++ b/bindings/metadata/Soup-2.4.metadata @@ -0,0 +1,3 @@ +AuthDomain.accepts skip +AuthDomain.challenge skip + diff --git a/bindings/metadata/WebKit2-4.0.metadata b/bindings/metadata/WebKit2-4.0.metadata new file mode 100644 index 00000000..c814abe0 --- /dev/null +++ b/bindings/metadata/WebKit2-4.0.metadata @@ -0,0 +1,19 @@ + +JavascriptResult + .get_global_context nullable=false unowned=true + .get_value nullable=false unowned=true + +//Forward upstream +Download + .failed#signal.error type="WebKit.DownloadError" +PrintCustomWidget + .apply#signal skip + .update#signal skip +PrintOperation + .failed#signal.error type="WebKit.PrintError" +WebContext.initialize_notification_permissions#method skip +WebResource + .failed#signal.error type="GLib.Error" +WebView + .load_failed#signal.error type="GLib.Error" + .show_option_menu#signal skip diff --git a/bindings/metadata/WebKit2WebExtension-4.0-custom.vala b/bindings/metadata/WebKit2WebExtension-4.0-custom.vala new file mode 100644 index 00000000..a994a774 --- /dev/null +++ b/bindings/metadata/WebKit2WebExtension-4.0-custom.vala @@ -0,0 +1,5 @@ +namespace WebKit { + namespace DOM { + public delegate void EventTargetFunc (WebKit.DOM.EventTarget target, WebKit.DOM.Event event); + } +} diff --git a/bindings/metadata/WebKit2WebExtension-4.0.metadata b/bindings/metadata/WebKit2WebExtension-4.0.metadata new file mode 100644 index 00000000..c496dba4 --- /dev/null +++ b/bindings/metadata/WebKit2WebExtension-4.0.metadata @@ -0,0 +1,9 @@ +DOM* parent="WebKit.DOM" name="DOM(.+)" + +DOMEventTarget.add_event_listener skip +_ContextMenu skip +_ContextMenuItem skip + +Frame.get_javascript_* nullable=false unowned=true + +DOMEventTarget.add_event_listener_with_closure.handler type="owned WebKit.DOM.EventTargetFunc" diff --git a/bindings/vapi/javascriptcore-4.0.vapi b/bindings/vapi/javascriptcore-4.0.vapi new file mode 100644 index 00000000..d152ce2a --- /dev/null +++ b/bindings/vapi/javascriptcore-4.0.vapi @@ -0,0 +1,155 @@ +/* + * Copyright 2017 Michael Gratton + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +[CCode (cprefix = "JS", + gir_namespace = "JavaScriptCore", + gir_version = "4.0", + lower_case_cprefix = "JS_", + cheader_filename = "JavaScriptCore/JavaScript.h")] +namespace JS { + + [CCode (cname = "JSContextRef")] + [SimpleType] + public struct Context { + + [CCode (cname = "JSEvaluateScript")] + public Value evaluate_script(String script, + Object? thisObject, + String? sourceURL, + int startingLineNumber, + out Value? exception); + + [CCode (cname = "JSCheckScriptSyntax")] + public Value check_script_syntax(String script, + String? sourceURL, + int startingLineNumber, + out Value? exception); + + } + + [CCode (cname = "JSGlobalContextRef")] + [SimpleType] + public struct GlobalContext : Context { + + [CCode (cname = "JSGlobalContextRetain")] + public bool retain(); + + [CCode (cname = "JSGlobalContextRelease")] + public bool release(); + + } + + [CCode (cname = "JSType", has_type_id = false)] + public enum Type { + + [CCode (cname = "kJSTypeUndefined")] + UNDEFINED, + + [CCode (cname = "kJSTypeNull")] + NULL, + + [CCode (cname = "kJSTypeBoolean")] + BOOLEAN, + + [CCode (cname = "kJSTypeNumber")] + NUMBER, + + [CCode (cname = "kJSTypeString")] + STRING, + + [CCode (cname = "kJSTypeObject")] + OBJECT + } + + [CCode (cname = "JSObjectRef")] + [SimpleType] + public struct Object { + + [CCode (cname = "JSObjectMakeFunction")] + public Object.make_function(String? name, + [CCode (array_length_pos=1.5)] + String[]? parameterNames, + String body, + String? sourceURL, + int startingLineNumber, + out Value? exception); + + [CCode (cname = "JSObjectCallAsFunction", instance_pos = 1.1)] + public Value call_as_function(Context ctx, + Object? thisObject, + [CCode (array_length_pos=2.5)] + Value[]? arguments, + out Value? exception); + + [CCode (cname = "JSObjectHasProperty", instance_pos = 1.1)] + public bool has_property(Context ctx, String property_name); + + [CCode (cname = "JSObjectGetProperty", instance_pos = 1.1)] + public Value get_property(Context ctx, + String property_name, + out Value? exception); + + } + + [CCode (cname = "JSValueRef")] + [SimpleType] + public struct Value { + + [CCode (cname = "JSValueGetType", instance_pos = 1.1)] + public Type get_type(Context context); + + [CCode (cname = "JSValueIsBoolean", instance_pos = 1.1)] + public bool is_boolean(Context ctx); + + [CCode (cname = "JSValueIsNumber", instance_pos = 1.1)] + public bool is_number(Context ctx); + + [CCode (cname = "JSValueIsObject", instance_pos = 1.1)] + public bool is_object(Context ctx); + + [CCode (cname = "JSValueIsString", instance_pos = 1.1)] + public bool is_string(Context ctx); + + [CCode (cname = "JSValueToBoolean", instance_pos = 1.1)] + public bool to_boolean(Context ctx); + + [CCode (cname = "JSValueToNumber", instance_pos = 1.1)] + public double to_number(Context ctx, out Value exception); + + [CCode (cname = "JSValueToObject", instance_pos = 1.1)] + public Object to_object(Context ctx, out Value exception); + + [CCode (cname = "JSValueToStringCopy", instance_pos = 1.1)] + public String to_string_copy(Context ctx, out Value exception); + + } + + [CCode (cname = "JSStringRef")] + [SimpleType] + public struct String { + + [CCode (cname = "JSStringCreateWithUTF8CString")] + public String.create_with_utf8_cstring(string str); + + [CCode (cname = "JSStringGetLength")] + public int String.get_length(); + + [CCode (cname = "JSStringGetMaximumUTF8CStringSize")] + public int String.get_maximum_utf8_cstring_size(); + + [CCode (cname = "JSStringGetUTF8CString")] + public void String.get_utf8_cstring(string* buffer, int bufferSize); + + [CCode (cname = "JSStringRetain")] + public void String.retain(); + + [CCode (cname = "JSStringRelease")] + public void String.release(); + + } + +} diff --git a/meson.build b/meson.build index 4f958d25..2bb4239c 100644 --- a/meson.build +++ b/meson.build @@ -59,6 +59,8 @@ gio = dependency('gio-2.0', version: '>=' + target_glib) gobject_introspection = dependency('gobject-introspection-1.0') gthread = dependency('gthread-2.0', version: '>=' + target_glib) javascriptcoregtk = dependency('javascriptcoregtk-4.0', version: '>=' + target_webkit) +javascriptcoregtk_lib = cc.find_library('javascriptcoregtk-4.0') +javascriptcoregtk_vapi = valac.find_library('javascriptcore-4.0', dirs: vapi_dir) libcanberra = dependency('libcanberra', version: '>= 0.28') libmath = cc.find_library('m') libnotify = dependency('libnotify', version: '>= 0.7.5') @@ -69,7 +71,8 @@ libunwind_generic_dep = dependency('libunwind-generic', version: '>= 1.1') libxml = dependency('libxml-2.0', version: '>= 2.7.8') posix = valac.find_library('posix') sqlite = dependency('sqlite3', version: '>= 3.12') -webkit2gtk_web_extension = dependency('webkit2gtk-web-extension-4.0', version: '>=' + target_webkit) +webkit2gtk_dep = dependency('webkit2gtk-4.0', version: '>=' + target_webkit) +webkit2gtk_web_extension_dep = dependency('webkit2gtk-web-extension-4.0', version: '>=' + target_webkit) # Libunwind system dependencies above ensures appropriate versions, # but this declared depencency is what we actually build against so we @@ -100,6 +103,9 @@ if enable_valadoc valadoc = find_program('valadoc') endif +# This will provide our custom dependencies, such as webkit2gtk +subdir('bindings') + # Language detection iso_codes_dir = join_paths('/', 'usr', 'share', 'xml', 'iso-codes') if iso_639_xml == '' diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fdf7148f..91c3d7dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -537,7 +537,7 @@ set(ENGINE_PACKAGES glib-2.0 gmime-2.6 libunwind - javascriptcoregtk-4.0 + javascriptcore-4.0 libxml-2.0 posix sqlite3 @@ -562,7 +562,7 @@ set(WEB_PROCESS_PACKAGES geary-engine gee-0.8 gtk+-3.0 - javascriptcoregtk-4.0 + javascriptcore-4.0 libsoup-2.4 webkit2gtk-web-extension-4.0 ) @@ -619,6 +619,7 @@ add_definitions(${CFLAGS}) set(VALAC_OPTIONS --vapidir=${CMAKE_BINARY_DIR}/src --vapidir=${CMAKE_SOURCE_DIR}/bindings/vapi + --metadatadir=${CMAKE_SOURCE_DIR}/bindings/metadata --target-glib=${TARGET_GLIB} --thread --debug @@ -655,6 +656,38 @@ set_property( ) target_link_libraries(geary-engine m ${DEPS_LIBRARIES} sqlite3-unicodesn) +# WebKit2GTK VAPI generation +################################################# +add_custom_target(webkit2gtk-vapi + DEPENDS + "${CMAKE_BINARY_DIR}/src/webkit2gtk-4.0.vapi" + "${CMAKE_BINARY_DIR}/src/webkit2gtk-web-extension-4.0.vapi" + "${CMAKE_SOURCE_DIR}/bindings/vapi/javascriptcore-4.0.vapi" +) +add_custom_command( + OUTPUT + ${CMAKE_BINARY_DIR}/src/webkit2gtk-4.0.vapi + DEPENDS + "${CMAKE_SOURCE_DIR}/bindings/metadata/WebKit2-4.0.metadata" + "${CMAKE_SOURCE_DIR}/bindings/vapi/javascriptcore-4.0.vapi" + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}/bindings/metadata" + COMMAND + vapigen --library=webkit2gtk-4.0 --pkg gtk+-3.0 --pkg libsoup-2.4 --pkg javascriptcore-4.0 --vapidir=${CMAKE_SOURCE_DIR}/bindings/vapi --metadatadir=${CMAKE_SOURCE_DIR}/bindings/metadata --directory=${CMAKE_BINARY_DIR}/src `${PKG_CONFIG_EXECUTABLE} --variable=girdir gobject-introspection-1.0`/WebKit2-4.0.gir +) +add_custom_command( + OUTPUT + "${CMAKE_BINARY_DIR}/src/webkit2gtk-web-extension-4.0.vapi" + DEPENDS + "${CMAKE_SOURCE_DIR}/bindings/metadata/WebKit2WebExtension-4.0.metadata" + "${CMAKE_SOURCE_DIR}/bindings/metadata/WebKit2WebExtension-4.0-custom.vala" + "${CMAKE_SOURCE_DIR}/bindings/vapi/javascriptcore-4.0.vapi" + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}/bindings/metadata" + COMMAND + vapigen --library=webkit2gtk-web-extension-4.0 --pkg gtk+-3.0 --pkg libsoup-2.4 --pkg javascriptcore-4.0 --vapidir=${CMAKE_SOURCE_DIR}/bindings/vapi --metadatadir=${CMAKE_SOURCE_DIR}/bindings/metadata --directory=${CMAKE_BINARY_DIR}/src `${PKG_CONFIG_EXECUTABLE} --variable=girdir gobject-introspection-1.0`/WebKit2WebExtension-4.0.gir WebKit2WebExtension-4.0-custom.vala +) + # Client library (static lib used for building client and unit tests) ################################################# @@ -671,7 +704,7 @@ OPTIONS ) add_library(geary-client STATIC ${CLIENT_VALA_C}) -add_dependencies(geary-client resource_copy) +add_dependencies(geary-client resource_copy webkit2gtk-vapi) target_link_libraries(geary-client m ${DEPS_LIBRARIES} geary-engine) # Main client application binary diff --git a/src/client/conversation-viewer/conversation-web-view.vala b/src/client/conversation-viewer/conversation-web-view.vala index 642eb7fd..0df3a21d 100644 --- a/src/client/conversation-viewer/conversation-web-view.vala +++ b/src/client/conversation-viewer/conversation-web-view.vala @@ -183,7 +183,7 @@ public class ConversationWebView : ClientWebView { private void on_deceptive_link_clicked(WebKit.JavascriptResult result) { try { - unowned JS.GlobalContext context = result.get_global_context(); + JS.GlobalContext context = result.get_global_context(); JS.Object details = WebKitUtil.to_object(result); uint reason = (uint) Geary.JS.to_number( diff --git a/src/client/util/util-webkit.vala b/src/client/util/util-webkit.vala index cba9eaf2..319e28ad 100644 --- a/src/client/util/util-webkit.vala +++ b/src/client/util/util-webkit.vala @@ -18,8 +18,8 @@ namespace WebKitUtil { */ public bool to_bool(WebKit.JavascriptResult result) throws Geary.JS.Error { - unowned JS.GlobalContext context = result.get_global_context(); - unowned JS.Value value = result.get_value(); + JS.GlobalContext context = result.get_global_context(); + JS.Value value = result.get_value(); if (!value.is_boolean(context)) { throw new Geary.JS.Error.TYPE("Result is not a JS Boolean object"); } @@ -59,12 +59,12 @@ namespace WebKitUtil { */ public string as_string(WebKit.JavascriptResult result) throws Geary.JS.Error { - unowned JS.GlobalContext context = result.get_global_context(); - unowned JS.Value js_str_value = result.get_value(); + JS.GlobalContext context = result.get_global_context(); + JS.Value js_str_value = result.get_value(); JS.Value? err = null; JS.String js_str = js_str_value.to_string_copy(context, out err); Geary.JS.check_exception(context, err); - return Geary.JS.to_string_released((owned) js_str); + return Geary.JS.to_string_released(js_str); } /** diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala index 1f478a6c..abd9ae42 100644 --- a/src/client/web-process/web-process-extension.vala +++ b/src/client/web-process/web-process-extension.vala @@ -87,9 +87,10 @@ public class GearyWebExtension : Object { bool should_load = false; WebKit.Frame frame = page.get_main_frame(); // Explicit cast fixes build on s390x/ppc64. Bug 783882 - unowned JS.GlobalContext context = frame.get_javascript_global_context(); + JS.GlobalContext context = (JS.GlobalContext) + frame.get_javascript_global_context(); try { - unowned JS.Value ret = execute_script( + JS.Value ret = execute_script( context, "geary.allowRemoteImages", int.parse("__LINE__") ); should_load = ret.to_boolean(context); @@ -105,7 +106,8 @@ public class GearyWebExtension : Object { private void remote_image_load_blocked(WebKit.WebPage page) { WebKit.Frame frame = page.get_main_frame(); // Explicit cast fixes build on s390x/ppc64. Bug 783882 - unowned JS.GlobalContext context = frame.get_javascript_global_context(); + JS.GlobalContext context = (JS.GlobalContext) + frame.get_javascript_global_context(); try { execute_script( context, "geary.remoteImageLoadBlocked();", int.parse("__LINE__") @@ -121,7 +123,8 @@ public class GearyWebExtension : Object { private void selection_changed(WebKit.WebPage page) { WebKit.Frame frame = page.get_main_frame(); // Explicit cast fixes build on s390x/ppc64. Bug 783882 - unowned JS.GlobalContext context = frame.get_javascript_global_context(); + JS.GlobalContext context = (JS.GlobalContext) + frame.get_javascript_global_context(); try { execute_script( context, "geary.selectionChanged();", int.parse("__LINE__") @@ -133,18 +136,20 @@ public class GearyWebExtension : Object { // Return type is nullable as a workaround for Bug 778046, it will // never actually be null. - private unowned JS.Value? execute_script(JS.Context context, string script, int line) + private JS.Value? execute_script(JS.Context context, string script, int line) throws Geary.JS.Error { - JS.String js_script = new JS.String.create_with_utf8_cstring(script); - JS.String js_source = new JS.String.create_with_utf8_cstring("__FILE__"); + JS.String js_script = JS.String.create_with_utf8_cstring(script); + JS.String js_source = JS.String.create_with_utf8_cstring("__FILE__"); JS.Value? err = null; try { - unowned JS.Value ret = context.evaluate_script( + JS.Value ret = context.evaluate_script( js_script, null, js_source, line, out err ); Geary.JS.check_exception(context, err); return ret; } finally { + js_script.release(); + js_source.release(); } } diff --git a/src/engine/util/util-js.vala b/src/engine/util/util-js.vala index dcfac4b1..e53ff747 100644 --- a/src/engine/util/util-js.vala +++ b/src/engine/util/util-js.vala @@ -72,7 +72,7 @@ namespace Geary.JS { global::JS.String js_str = value.to_string_copy(context, out err); Geary.JS.check_exception(context, err); - return Geary.JS.to_string_released((owned) js_str); + return Geary.JS.to_string_released(js_str); } /** @@ -101,11 +101,12 @@ namespace Geary.JS { /** * Returns a JSC {@link JS.String} as a Vala {@link string}. */ - public inline string to_string_released(owned global::JS.String js) { - size_t len = js.get_maximum_utf8_cstring_size(); - uint8[] str = new uint8[len]; - js.get_utf8_cstring(ref str); - return (string) str; + public inline string to_string_released(global::JS.String js) { + int len = js.get_maximum_utf8_cstring_size(); + string str = string.nfill(len, 0); + js.get_utf8_cstring(str, len); + js.release(); + return str; } /** @@ -121,12 +122,13 @@ namespace Geary.JS { global::JS.Object object, string name) throws Geary.JS.Error { - global::JS.String js_name = new global::JS.String.create_with_utf8_cstring(name); + global::JS.String js_name = global::JS.String.create_with_utf8_cstring(name); global::JS.Value? err = null; global::JS.Value prop = object.get_property(context, js_name, out err); try { Geary.JS.check_exception(context, err); } finally { + js_name.release(); } return prop; } @@ -155,7 +157,7 @@ namespace Geary.JS { throw new Error.EXCEPTION( "JS exception thrown [%s]: %s" - .printf(err_type.to_string(), to_string_released((owned) err_str)) + .printf(err_type.to_string(), to_string_released(err_str)) ); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7bbace78..381c87c7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -87,7 +87,7 @@ set(TEST_ENGINE_PACKAGES gio-2.0 glib-2.0 gmime-2.6 - javascriptcoregtk-4.0 + javascriptcore-4.0 libunwind libxml-2.0 sqlite3