Clean up JavaScriptCore VAPI, client and engine code.
* bindings/vapi/javascriptcore-4.0.vapi: Make JS objects match their JSC definitions: move JSValueFoo methods to JS.Value, etc. Update call sites. * src/client/util/util-webkit.vala: Move WebKit-specific common methods from ClientWebView here. Update call sites. * src/engine/util/util-js.vala: Move JSC-specific common methods from ClientWebView and ComposerPageStateTest here. Update call sites. * src/client/web-process/web-process-extension.vala: Check for and handle exceptions when calling JS code. * src/CMakeLists.txt: Add new source files, make WebKit VAPI generation and engine compilation depend on JSC.
This commit is contained in:
parent
22de6b122e
commit
772b874def
9 changed files with 311 additions and 179 deletions
|
|
@ -1,4 +1,9 @@
|
||||||
/* javascriptcore-4.0.vapi. */
|
/*
|
||||||
|
* Copyright 2017 Michael Gratton <mike@vee.net>
|
||||||
|
*
|
||||||
|
* 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")]
|
[CCode (cprefix = "JS", gir_namespace = "JavaScriptCore", gir_version = "4.0", lower_case_cprefix = "JS_", cheader_filename = "JavaScriptCore/JavaScript.h")]
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
@ -7,27 +12,6 @@ namespace JS {
|
||||||
[SimpleType]
|
[SimpleType]
|
||||||
public struct Context {
|
public struct Context {
|
||||||
|
|
||||||
[CCode (cname = "JSValueIsBoolean")]
|
|
||||||
public bool is_boolean(JS.Value value);
|
|
||||||
|
|
||||||
[CCode (cname = "JSValueIsNumber")]
|
|
||||||
public bool is_number(JS.Value value);
|
|
||||||
|
|
||||||
[CCode (cname = "JSValueIsObject")]
|
|
||||||
public bool is_object(JS.Value value);
|
|
||||||
|
|
||||||
[CCode (cname = "JSValueToBoolean")]
|
|
||||||
public bool to_boolean(JS.Value value);
|
|
||||||
|
|
||||||
[CCode (cname = "JSValueToNumber")]
|
|
||||||
public double to_number(JS.Value value, out JS.Value exception);
|
|
||||||
|
|
||||||
[CCode (cname = "JSValueToObject")]
|
|
||||||
public Object to_object(JS.Value value, out JS.Value exception);
|
|
||||||
|
|
||||||
[CCode (cname = "JSValueToStringCopy")]
|
|
||||||
public String to_string_copy(JS.Value value, out JS.Value exception);
|
|
||||||
|
|
||||||
[CCode (cname = "JSEvaluateScript")]
|
[CCode (cname = "JSEvaluateScript")]
|
||||||
public Value evaluate_script(String script,
|
public Value evaluate_script(String script,
|
||||||
Object? thisObject,
|
Object? thisObject,
|
||||||
|
|
@ -35,21 +19,11 @@ namespace JS {
|
||||||
int startingLineNumber,
|
int startingLineNumber,
|
||||||
out Value? exception);
|
out Value? exception);
|
||||||
|
|
||||||
[CCode (cname = "JSObjectMakeFunction")]
|
[CCode (cname = "JSCheckScriptSyntax")]
|
||||||
public Object make_function(String? name,
|
public Value check_script_syntax(String script,
|
||||||
[CCode (array_length_pos=1.5)]
|
String? sourceURL,
|
||||||
String[]? parameterNames,
|
int startingLineNumber,
|
||||||
String body,
|
out Value? exception);
|
||||||
String? sourceURL,
|
|
||||||
int startingLineNumber,
|
|
||||||
out Value? exception);
|
|
||||||
|
|
||||||
[CCode (cname = "JSObjectCallAsFunction")]
|
|
||||||
public Value call_as_function(Object object,
|
|
||||||
Object? thisObject,
|
|
||||||
[CCode (array_length_pos=2.5)]
|
|
||||||
Value[]? arguments,
|
|
||||||
out Value? exception);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,10 +35,48 @@ namespace JS {
|
||||||
public bool release();
|
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")]
|
[CCode (cname = "JSObjectRef")]
|
||||||
[SimpleType]
|
[SimpleType]
|
||||||
public struct Object {
|
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)]
|
[CCode (cname = "JSObjectHasProperty", instance_pos = 1.1)]
|
||||||
public bool has_property(Context ctx, String property_name);
|
public bool has_property(Context ctx, String property_name);
|
||||||
|
|
||||||
|
|
@ -80,7 +92,31 @@ namespace JS {
|
||||||
public struct Value {
|
public struct Value {
|
||||||
|
|
||||||
[CCode (cname = "JSValueGetType", instance_pos = 1.1)]
|
[CCode (cname = "JSValueGetType", instance_pos = 1.1)]
|
||||||
public JS.Type get_type(JS.Context context);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,26 +144,4 @@ namespace JS {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,7 @@ engine/util/util-html.vala
|
||||||
engine/util/util-imap-utf7.vala
|
engine/util/util-imap-utf7.vala
|
||||||
engine/util/util-inet.vala
|
engine/util/util-inet.vala
|
||||||
engine/util/util-iterable.vala
|
engine/util/util-iterable.vala
|
||||||
|
engine/util/util-js.vala
|
||||||
engine/util/util-numeric.vala
|
engine/util/util-numeric.vala
|
||||||
engine/util/util-object.vala
|
engine/util/util-object.vala
|
||||||
engine/util/util-reference-semantics.vala
|
engine/util/util-reference-semantics.vala
|
||||||
|
|
@ -404,6 +405,7 @@ client/util/util-gravatar.vala
|
||||||
client/util/util-gtk.vala
|
client/util/util-gtk.vala
|
||||||
client/util/util-international.vala
|
client/util/util-international.vala
|
||||||
client/util/util-migrate.vala
|
client/util/util-migrate.vala
|
||||||
|
client/util/util-webkit.vala
|
||||||
)
|
)
|
||||||
|
|
||||||
set(WEB_PROCESS_SRC
|
set(WEB_PROCESS_SRC
|
||||||
|
|
@ -522,6 +524,7 @@ set(ENGINE_PACKAGES
|
||||||
gio-2.0
|
gio-2.0
|
||||||
glib-2.0
|
glib-2.0
|
||||||
gmime-2.6
|
gmime-2.6
|
||||||
|
javascriptcore-4.0
|
||||||
libxml-2.0
|
libxml-2.0
|
||||||
posix
|
posix
|
||||||
sqlite3
|
sqlite3
|
||||||
|
|
@ -533,7 +536,6 @@ set(CLIENT_PACKAGES
|
||||||
geary-engine
|
geary-engine
|
||||||
gio-2.0
|
gio-2.0
|
||||||
gtk+-3.0
|
gtk+-3.0
|
||||||
javascriptcore-4.0
|
|
||||||
libcanberra
|
libcanberra
|
||||||
libnotify
|
libnotify
|
||||||
libsecret-1
|
libsecret-1
|
||||||
|
|
@ -645,6 +647,7 @@ add_custom_target(webkit2gtk-vapi
|
||||||
DEPENDS
|
DEPENDS
|
||||||
"${CMAKE_BINARY_DIR}/src/webkit2gtk-4.0.vapi"
|
"${CMAKE_BINARY_DIR}/src/webkit2gtk-4.0.vapi"
|
||||||
"${CMAKE_BINARY_DIR}/src/webkit2gtk-web-extension-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(
|
add_custom_command(
|
||||||
OUTPUT
|
OUTPUT
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected errordomain JSError { EXCEPTION, TYPE }
|
|
||||||
|
|
||||||
public class ClientWebView : WebKit.WebView {
|
public class ClientWebView : WebKit.WebView {
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -105,43 +103,6 @@ public class ClientWebView : WebKit.WebView {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static bool get_bool_result(WebKit.JavascriptResult result)
|
|
||||||
throws JSError {
|
|
||||||
JS.GlobalContext context = result.get_global_context();
|
|
||||||
JS.Value value = result.get_value();
|
|
||||||
return context.to_boolean(value);
|
|
||||||
// XXX unref result?
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static int get_int_result(WebKit.JavascriptResult result)
|
|
||||||
throws JSError {
|
|
||||||
JS.GlobalContext context = result.get_global_context();
|
|
||||||
JS.Value value = result.get_value();
|
|
||||||
if (!context.is_number(value)) {
|
|
||||||
throw new JSError.TYPE("Value is not a number");
|
|
||||||
}
|
|
||||||
JS.Value? err = null;
|
|
||||||
return (int) context.to_number(value, out err);
|
|
||||||
// XXX check err
|
|
||||||
// XXX unref result?
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static string? get_string_result(WebKit.JavascriptResult result)
|
|
||||||
throws JSError {
|
|
||||||
JS.GlobalContext context = result.get_global_context();
|
|
||||||
JS.Value js_str_value = result.get_value();
|
|
||||||
JS.Value? err = null;
|
|
||||||
JS.String js_str = context.to_string_copy(js_str_value, out err);
|
|
||||||
// XXX check err
|
|
||||||
int len = js_str.get_maximum_utf8_cstring_size();
|
|
||||||
string value = string.nfill(len, 0);
|
|
||||||
js_str.get_utf8_cstring(value, len);
|
|
||||||
js_str.release();
|
|
||||||
debug("Got string: %s", value);
|
|
||||||
return value;
|
|
||||||
// XXX unref result?
|
|
||||||
}
|
|
||||||
|
|
||||||
private static inline uint to_wk2_font_size(Pango.FontDescription font) {
|
private static inline uint to_wk2_font_size(Pango.FontDescription font) {
|
||||||
Gdk.Screen? screen = Gdk.Screen.get_default();
|
Gdk.Screen? screen = Gdk.Screen.get_default();
|
||||||
double dpi = screen != null ? screen.get_resolution() : 96.0;
|
double dpi = screen != null ? screen.get_resolution() : 96.0;
|
||||||
|
|
@ -245,9 +206,9 @@ public class ClientWebView : WebKit.WebView {
|
||||||
content_manager.script_message_received[PREFERRED_HEIGHT_MESSAGE].connect(
|
content_manager.script_message_received[PREFERRED_HEIGHT_MESSAGE].connect(
|
||||||
(result) => {
|
(result) => {
|
||||||
try {
|
try {
|
||||||
this.preferred_height = get_int_result(result);
|
this.preferred_height = (int) WebKitUtil.to_number(result);
|
||||||
queue_resize();
|
queue_resize();
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
debug("Could not get preferred height: %s", err.message);
|
debug("Could not get preferred height: %s", err.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -258,8 +219,8 @@ public class ClientWebView : WebKit.WebView {
|
||||||
content_manager.script_message_received[SELECTION_CHANGED_MESSAGE].connect(
|
content_manager.script_message_received[SELECTION_CHANGED_MESSAGE].connect(
|
||||||
(result) => {
|
(result) => {
|
||||||
try {
|
try {
|
||||||
selection_changed(get_bool_result(result));
|
selection_changed(WebKitUtil.to_bool(result));
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
debug("Could not get selection content: %s", err.message);
|
debug("Could not get selection content: %s", err.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ public class ComposerWebView : ClientWebView {
|
||||||
WebKit.JavascriptResult result = yield this.run_javascript(
|
WebKit.JavascriptResult result = yield this.run_javascript(
|
||||||
"geary.getHtml();", null
|
"geary.getHtml();", null
|
||||||
);
|
);
|
||||||
return get_string_result(result);
|
return WebKitUtil.to_string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -210,7 +210,7 @@ public class ComposerWebView : ClientWebView {
|
||||||
WebKit.JavascriptResult result = yield this.run_javascript(
|
WebKit.JavascriptResult result = yield this.run_javascript(
|
||||||
"geary.getText();", null
|
"geary.getText();", null
|
||||||
);
|
);
|
||||||
return get_string_result(result);
|
return WebKitUtil.to_string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ public class ConversationWebView : ClientWebView {
|
||||||
WebKit.JavascriptResult result = yield this.run_javascript(
|
WebKit.JavascriptResult result = yield this.run_javascript(
|
||||||
"geary.getSelectionForFind();", null
|
"geary.getSelectionForFind();", null
|
||||||
);
|
);
|
||||||
return get_string_result(result);
|
return WebKitUtil.to_string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,7 +54,7 @@ public class ConversationWebView : ClientWebView {
|
||||||
WebKit.JavascriptResult result = yield this.run_javascript(
|
WebKit.JavascriptResult result = yield this.run_javascript(
|
||||||
"geary.getSelectionForQuoting();", null
|
"geary.getSelectionForQuoting();", null
|
||||||
);
|
);
|
||||||
return get_string_result(result);
|
return WebKitUtil.to_string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
87
src/client/util/util-webkit.vala
Normal file
87
src/client/util/util-webkit.vala
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Michael James Gratton <mike@vee.net>
|
||||||
|
*
|
||||||
|
* This software is licensed under the GNU Lesser General Public License
|
||||||
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility functions for WebKit objects.
|
||||||
|
*/
|
||||||
|
namespace WebKitUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a WebKit {@link WebKit.JavascriptResult} as a `bool`.
|
||||||
|
*
|
||||||
|
* This will raise a {@link Geary.JS.Error.TYPE} error if the
|
||||||
|
* result is not a JavaScript `Boolean`.
|
||||||
|
*/
|
||||||
|
public bool to_bool(WebKit.JavascriptResult result)
|
||||||
|
throws Geary.JS.Error {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
return value.to_boolean(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a WebKit {@link WebKit.JavascriptResult} as a `double`.
|
||||||
|
*
|
||||||
|
* This will raise a {@link Geary.JS.Error.TYPE} error if the
|
||||||
|
* result is not a JavaScript `Number`.
|
||||||
|
*/
|
||||||
|
public double to_number(WebKit.JavascriptResult result)
|
||||||
|
throws Geary.JS.Error {
|
||||||
|
JS.GlobalContext context = result.get_global_context();
|
||||||
|
JS.Value value = result.get_value();
|
||||||
|
if (!value.is_number(context)) {
|
||||||
|
throw new Geary.JS.Error.TYPE("Result is not a JS Number object");
|
||||||
|
}
|
||||||
|
|
||||||
|
JS.Value? err = null;
|
||||||
|
double number = value.to_number(context, out err);
|
||||||
|
Geary.JS.check_exception(context, err);
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a WebKit {@link WebKit.JavascriptResult} as a Vala {@link string}.
|
||||||
|
*
|
||||||
|
* This will raise a {@link Geary.JS.Error.TYPE} error if the
|
||||||
|
* result is not a JavaScript `String`.
|
||||||
|
*/
|
||||||
|
public string? to_string(WebKit.JavascriptResult result)
|
||||||
|
throws Geary.JS.Error {
|
||||||
|
JS.GlobalContext context = result.get_global_context();
|
||||||
|
JS.Value js_str_value = result.get_value();
|
||||||
|
if (!js_str_value.is_string(context)) {
|
||||||
|
throw new Geary.JS.Error.TYPE("Result is not a JS String object");
|
||||||
|
}
|
||||||
|
|
||||||
|
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(js_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a WebKit {@link WebKit.JavascriptResult} to a {@link string}.
|
||||||
|
*
|
||||||
|
* Unlike the other `get_foo_result` methods, this will coax the
|
||||||
|
* result to a string, effectively by calling the JavaScript
|
||||||
|
* `toString()` method on it, and returning that value.
|
||||||
|
*/
|
||||||
|
public string? as_string(WebKit.JavascriptResult result)
|
||||||
|
throws Geary.JS.Error {
|
||||||
|
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(js_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -81,33 +81,65 @@ public class GearyWebExtension : Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool should_load_remote_images(WebKit.WebPage page) {
|
private bool should_load_remote_images(WebKit.WebPage page) {
|
||||||
|
bool should_load = false;
|
||||||
WebKit.Frame frame = page.get_main_frame();
|
WebKit.Frame frame = page.get_main_frame();
|
||||||
JS.GlobalContext context = frame.get_javascript_global_context();
|
JS.GlobalContext context = frame.get_javascript_global_context();
|
||||||
JS.Value ret = execute_script(context, "geary.allowRemoteImages");
|
try {
|
||||||
// XXX check err here, log it
|
JS.Value ret = execute_script(
|
||||||
//JS.Value? err;
|
context, "geary.allowRemoteImages", int.parse("__LINE__")
|
||||||
//return context.to_boolean(ret, err);
|
);
|
||||||
return context.to_boolean(ret);
|
should_load = ret.to_boolean(context);
|
||||||
|
} catch (Error err) {
|
||||||
|
debug(
|
||||||
|
"Error checking PageState::allowRemoteImages: %s",
|
||||||
|
err.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return should_load;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void remote_image_load_blocked(WebKit.WebPage page) {
|
private void remote_image_load_blocked(WebKit.WebPage page) {
|
||||||
WebKit.Frame frame = page.get_main_frame();
|
WebKit.Frame frame = page.get_main_frame();
|
||||||
JS.Context context = frame.get_javascript_global_context();
|
JS.GlobalContext context = frame.get_javascript_global_context();
|
||||||
execute_script(context, "geary.remoteImageLoadBlocked();");
|
try {
|
||||||
|
execute_script(
|
||||||
|
context, "geary.remoteImageLoadBlocked();", int.parse("__LINE__")
|
||||||
|
);
|
||||||
|
} catch (Error err) {
|
||||||
|
debug(
|
||||||
|
"Error calling PageState::remoteImageLoadBlocked: %s",
|
||||||
|
err.message
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selection_changed(WebKit.WebPage page) {
|
private void selection_changed(WebKit.WebPage page) {
|
||||||
WebKit.Frame frame = page.get_main_frame();
|
WebKit.Frame frame = page.get_main_frame();
|
||||||
JS.Context context = frame.get_javascript_global_context();
|
JS.GlobalContext context = frame.get_javascript_global_context();
|
||||||
execute_script(context, "geary.selectionChanged();");
|
try {
|
||||||
|
execute_script(
|
||||||
|
context, "geary.selectionChanged();", int.parse("__LINE__")
|
||||||
|
);
|
||||||
|
} catch (Error err) {
|
||||||
|
debug("Error calling PageStates::selectionChanged: %s", err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private JS.Value execute_script(JS.Context context, string script) {
|
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_script = new JS.String.create_with_utf8_cstring(script);
|
||||||
// XXX check err here, log it
|
JS.String js_source = new JS.String.create_with_utf8_cstring("__FILE__");
|
||||||
//JS.Value? err;
|
JS.Value? err = null;
|
||||||
//context.evaluate_script(js_script, null, null, 0, out err);
|
try {
|
||||||
return context.evaluate_script(js_script, null, null, 0, null);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
79
src/engine/util/util-js.vala
Normal file
79
src/engine/util/util-js.vala
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Michael James Gratton <mike@vee.net>
|
||||||
|
*
|
||||||
|
* This software is licensed under the GNU Lesser General Public License
|
||||||
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility functions for WebKit JavaScriptCore (JSC) objects.
|
||||||
|
*/
|
||||||
|
namespace Geary.JS {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Errors produced by functions in {@link Geary.JS}.
|
||||||
|
*/
|
||||||
|
public errordomain Error {
|
||||||
|
/**
|
||||||
|
* A JS exception was thrown performing a function call.
|
||||||
|
*/
|
||||||
|
EXCEPTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link JS.Value} was not of the expected type.
|
||||||
|
*/
|
||||||
|
TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a {@link JS.Value} object is {{{null}}}.
|
||||||
|
*
|
||||||
|
* @return `true` if `js` is `null` or has a {@link JS.Type} of
|
||||||
|
* `NULL` according to `context`.
|
||||||
|
*/
|
||||||
|
public inline bool is_null(global::JS.Context context,
|
||||||
|
global::JS.Value? js) {
|
||||||
|
return (js == null || js.get_type(context) == global::JS.Type.NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a JSC {@link JS.String} as a Vala {@link string}.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks an JS exception returned from a JSC call.
|
||||||
|
*
|
||||||
|
* This method will raise a {@link Geary.JS.Error} if the given
|
||||||
|
* `err_value` is not null (in a Vala or JS sense).
|
||||||
|
*/
|
||||||
|
public inline void check_exception(global::JS.Context context,
|
||||||
|
global::JS.Value? err_value)
|
||||||
|
throws Error {
|
||||||
|
if (!is_null(context, err_value)) {
|
||||||
|
global::JS.Value? nested_err = null;
|
||||||
|
global::JS.Type err_type = err_value.get_type(context);
|
||||||
|
global::JS.String err_str =
|
||||||
|
err_value.to_string_copy(context, out nested_err);
|
||||||
|
|
||||||
|
if (!is_null(context, nested_err)) {
|
||||||
|
throw new Error.EXCEPTION(
|
||||||
|
"Nested exception getting exception %s as a string",
|
||||||
|
err_type.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error.EXCEPTION(
|
||||||
|
"JS exception thrown [%s]: %s"
|
||||||
|
.printf(err_type.to_string(), to_string_released(err_str))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -41,8 +41,8 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
load_body_fixture(html);
|
load_body_fixture(html);
|
||||||
try {
|
try {
|
||||||
assert(run_javascript(@"window.geary.getHtml();") == html + "<br><br>");
|
assert(run_javascript(@"window.geary.getHtml();") == html + "<br><br>");
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
print("JSError: %s", err.message);
|
print("Geary.JS.Error: %s", err.message);
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
print("WKError: %s", err.message);
|
print("WKError: %s", err.message);
|
||||||
|
|
@ -54,8 +54,8 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
load_body_fixture("<p>para</p>");
|
load_body_fixture("<p>para</p>");
|
||||||
try {
|
try {
|
||||||
assert(run_javascript(@"window.geary.getText();") == "para\n\n\n\n\n");
|
assert(run_javascript(@"window.geary.getText();") == "para\n\n\n\n\n");
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
print("JSError: %s", err.message);
|
print("Geary.JS.Error: %s", err.message);
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
print("WKError: %s", err.message);
|
print("WKError: %s", err.message);
|
||||||
|
|
@ -68,8 +68,8 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
try {
|
try {
|
||||||
assert(run_javascript(@"window.geary.getText();") ==
|
assert(run_javascript(@"window.geary.getText();") ==
|
||||||
"pre\n\n> quote\n> \npost\n\n\n\n\n");
|
"pre\n\n> quote\n> \npost\n\n\n\n\n");
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
print("JSError: %s", err.message);
|
print("Geary.JS.Error: %s", err.message);
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
print("WKError: %s", err.message);
|
print("WKError: %s", err.message);
|
||||||
|
|
@ -82,8 +82,8 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
try {
|
try {
|
||||||
assert(run_javascript(@"window.geary.getText();") ==
|
assert(run_javascript(@"window.geary.getText();") ==
|
||||||
"pre\n\n> quote1\n> \n>> quote2\n>> \npost\n\n\n\n\n");
|
"pre\n\n> quote1\n> \n>> quote2\n>> \npost\n\n\n\n\n");
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
print("JSError: %s", err.message);
|
print("Geary.JS.Error: %s", err.message);
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
print("WKError: %s", err.message);
|
print("WKError: %s", err.message);
|
||||||
|
|
@ -116,8 +116,8 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
@"foo\n$(q_marker)quote1\nbar");
|
@"foo\n$(q_marker)quote1\nbar");
|
||||||
assert(run_javascript(@"ComposerPageState.resolveNesting('$(js_cosy_quote2)', $(js_values));") ==
|
assert(run_javascript(@"ComposerPageState.resolveNesting('$(js_cosy_quote2)', $(js_values));") ==
|
||||||
@"foo\n$(q_marker)quote1\n$(q_marker)quote2\nbar");
|
@"foo\n$(q_marker)quote1\n$(q_marker)quote2\nbar");
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
print("JSError: %s", err.message);
|
print("Geary.JS.Error: %s", err.message);
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
print("WKError: %s", err.message);
|
print("WKError: %s", err.message);
|
||||||
|
|
@ -135,8 +135,8 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
@"$(q_marker)line1");
|
@"$(q_marker)line1");
|
||||||
assert(run_javascript("ComposerPageState.quoteLines('line1\\nline2');") ==
|
assert(run_javascript("ComposerPageState.quoteLines('line1\\nline2');") ==
|
||||||
@"$(q_marker)line1\n$(q_marker)line2");
|
@"$(q_marker)line1\n$(q_marker)line2");
|
||||||
} catch (JSError err) {
|
} catch (Geary.JS.Error err) {
|
||||||
print("JSError: %s", err.message);
|
print("Geary.JS.Error: %s", err.message);
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
print("WKError: %s", err.message);
|
print("WKError: %s", err.message);
|
||||||
|
|
@ -158,7 +158,7 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
|
|
||||||
WebKit.JavascriptResult result =
|
WebKit.JavascriptResult result =
|
||||||
this.test_view.run_javascript.end(async_result());
|
this.test_view.run_javascript.end(async_result());
|
||||||
return get_string_result(result);
|
return WebKitUtil.to_string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void async_complete(AsyncResult result) {
|
protected void async_complete(AsyncResult result) {
|
||||||
|
|
@ -174,48 +174,4 @@ class ComposerPageStateTest : Gee.TestCase {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static string? get_string_result(WebKit.JavascriptResult result)
|
|
||||||
throws JSError {
|
|
||||||
JS.GlobalContext context = result.get_global_context();
|
|
||||||
JS.Value js_str_value = result.get_value();
|
|
||||||
JS.Value? err = null;
|
|
||||||
JS.String js_str = context.to_string_copy(js_str_value, out err);
|
|
||||||
|
|
||||||
check_exception(context, err);
|
|
||||||
return to_string_released(js_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static inline void check_exception(JS.Context exe, JS.Value? err_value)
|
|
||||||
throws JSError {
|
|
||||||
if (!is_null(exe, err_value)) {
|
|
||||||
JS.Value? nested_err = null;
|
|
||||||
JS.Type err_type = err_value.get_type(exe);
|
|
||||||
JS.String err_str = exe.to_string_copy(err_value, out nested_err);
|
|
||||||
|
|
||||||
if (!is_null(exe, nested_err)) {
|
|
||||||
throw new JSError.EXCEPTION(
|
|
||||||
"Nested exception getting exception %s as a string",
|
|
||||||
err_type.to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new JSError.EXCEPTION(
|
|
||||||
"JS exception thrown [%s]: %s"
|
|
||||||
.printf(err_type.to_string(), to_string_released(err_str))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static inline bool is_null(JS.Context exe, JS.Value? js) {
|
|
||||||
return (js == null || js.get_type(exe) == JS.Type.NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static string to_string_released(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue