diff options
| author | Leo Ufimtsev | 2017-01-24 17:23:26 +0000 |
|---|---|---|
| committer | Alexander Kurtakov | 2017-02-07 07:18:01 +0000 |
| commit | 9b9501b0fef312da72586299e22d329f00ac5e20 (patch) | |
| tree | 2a399c24a5820a37dc1aa420dfe1db0f1ba62146 | |
| parent | a647bf412269ed1dee039baacd08a7c5472595bb (diff) | |
| download | eclipse.platform.swt-9b9501b0fef312da72586299e22d329f00ac5e20.tar.gz eclipse.platform.swt-9b9501b0fef312da72586299e22d329f00ac5e20.tar.xz eclipse.platform.swt-9b9501b0fef312da72586299e22d329f00ac5e20.zip | |
Bug 508217 Implement webkit2 support for browser function
(Part 1: JavaScript to call Java)
This implements Browser.function() on webkit2.
Current implementation allows javascript to reach java, but java doesn't
provide a return vaulue in the callback.
For detailed architecture changes, see: Webkit2JavaCallback() inside
WebKit.java.
In short, in webkit1 an 'external' object was registered and callJava
was a property of this object.
In webkit2, 'external' object is no longer used and instead a
callback/signal is registered/connected.
With this patch, the following jUnits pass:
test_BrowserFunction_callback
test_BrowserFunction_callback_with_integer
test_BrowserFunction_callback_with_boolean
test_BrowserFunction_callback_with_String
test_BrowserFunction_callback_with_multipleValues
testBrowser4.
The following does not:
test_BrowserFunction_callback_with_javaReturningInt
Patch set 4:
- I noticed that after a page re-load, webkit2 custom functions did not
work. This is because the page-reload mechanism has changed (Bg:510972)
but in the ported webkit2 mechanism functions were not re-registered).
- Added code to re-register custom functions after page reload.
Patch set 7:
- Removed 'protected' modifier.
- Moved comments around.
Change-Id: I94549ed306e0095c735d18389b55a36923c5ec08
Signed-off-by: Leo Ufimtsev <lufimtse@redhat.com>
7 files changed, 540 insertions, 88 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WebBrowser.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WebBrowser.java index 5f79e68379..df7f31745e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WebBrowser.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/common/org/eclipse/swt/browser/WebBrowser.java @@ -346,8 +346,10 @@ public void createFunction (BrowserFunction function) { functionBuffer.append (ERROR_ID.length ()); functionBuffer.append (")); throw error;} return result;};"); //$NON-NLS-1$ - StringBuffer buffer = new StringBuffer ("if (!window.callJava) {window.callJava = function callJava(index, token, args) {"); //$NON-NLS-1$ - buffer.append ("return external.callJava(index,token,args);}};"); //$NON-NLS-1$ + String javaCallDeclaration = getJavaCallDeclaration(); + + StringBuffer buffer = new StringBuffer (); //$NON-NLS-1$ + buffer.append (javaCallDeclaration); //$NON-NLS-1$ if (function.top) { buffer.append (functionBuffer.toString ()); } @@ -375,6 +377,18 @@ public void createFunction (BrowserFunction function) { execute (function.functionString); } +/** + * Designed to be overriden. + * @return javaScrit code that defines the 'callJava' syntax for javascript. + */ +String getJavaCallDeclaration() { + return "if (!window.callJava) {\n" + + " window.callJava = function callJava(index, token, args) {\n" + + " return external.callJava(index,token,args);\n" + + " }\n" + + "};\n"; +} + void deregisterFunction (BrowserFunction function) { functions.remove (function.index); } @@ -396,6 +410,9 @@ public Object evaluate (String script, boolean trusted) throws SWTException { } public Object evaluate (String script) throws SWTException { + // Developer note: + // Webkit1 & Mozilla use this mechanism. + // Webkit2 uses a different mechanism. See WebKit:evaluate(); BrowserFunction function = new EvaluateFunction (browser, ""); // $NON-NLS-1$ int index = getNextFunctionIndex (); function.index = index; diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c index 0d724f982a..694ed05d08 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c +++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk.c @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corporation and others. All rights reserved. + * Copyright (c) 2009, 2017 IBM Corporation and others. All rights reserved. * The contents of this file are made available under the terms * of the GNU Lesser General Public License (LGPL) Version 2.1 that * accompanies this distribution (lgpl-v21.txt). The LGPL is also @@ -1754,6 +1754,46 @@ JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1hit_1test_1result_1get_1li } #endif +#ifndef NO__1webkit_1javascript_1result_1get_1global_1context +JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1javascript_1result_1get_1global_1context) + (JNIEnv *env, jclass that, jintLong arg0) +{ + jintLong rc = 0; + WebKitGTK_NATIVE_ENTER(env, that, _1webkit_1javascript_1result_1get_1global_1context_FUNC); +/* + rc = (jintLong)webkit_javascript_result_get_global_context((gpointer)arg0); +*/ + { + WebKitGTK_LOAD_FUNCTION(fp, webkit_javascript_result_get_global_context) + if (fp) { + rc = (jintLong)((jintLong (CALLING_CONVENTION*)(gpointer))fp)((gpointer)arg0); + } + } + WebKitGTK_NATIVE_EXIT(env, that, _1webkit_1javascript_1result_1get_1global_1context_FUNC); + return rc; +} +#endif + +#ifndef NO__1webkit_1javascript_1result_1get_1value +JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1javascript_1result_1get_1value) + (JNIEnv *env, jclass that, jintLong arg0) +{ + jintLong rc = 0; + WebKitGTK_NATIVE_ENTER(env, that, _1webkit_1javascript_1result_1get_1value_FUNC); +/* + rc = (jintLong)webkit_javascript_result_get_value((gpointer)arg0); +*/ + { + WebKitGTK_LOAD_FUNCTION(fp, webkit_javascript_result_get_value) + if (fp) { + rc = (jintLong)((jintLong (CALLING_CONVENTION*)(gpointer))fp)((gpointer)arg0); + } + } + WebKitGTK_NATIVE_EXIT(env, that, _1webkit_1javascript_1result_1get_1value_FUNC); + return rc; +} +#endif + #ifndef NO__1webkit_1javascript_1result_1unref JNIEXPORT void JNICALL WebKitGTK_NATIVE(_1webkit_1javascript_1result_1unref) (JNIEnv *env, jclass that, jintLong arg0) @@ -2096,6 +2136,50 @@ JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1uri_1response_1get_1mime_1 } #endif +#ifndef NO__1webkit_1user_1content_1manager_1new +JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1user_1content_1manager_1new) + (JNIEnv *env, jclass that) +{ + jintLong rc = 0; + WebKitGTK_NATIVE_ENTER(env, that, _1webkit_1user_1content_1manager_1new_FUNC); +/* + rc = (jintLong)webkit_user_content_manager_new(); +*/ + { + WebKitGTK_LOAD_FUNCTION(fp, webkit_user_content_manager_new) + if (fp) { + rc = (jintLong)((jintLong (CALLING_CONVENTION*)())fp)(); + } + } + WebKitGTK_NATIVE_EXIT(env, that, _1webkit_1user_1content_1manager_1new_FUNC); + return rc; +} +#endif + +#ifndef NO__1webkit_1user_1content_1manager_1register_1script_1message_1handler +JNIEXPORT jboolean JNICALL WebKitGTK_NATIVE(_1webkit_1user_1content_1manager_1register_1script_1message_1handler) + (JNIEnv *env, jclass that, jintLong arg0, jbyteArray arg1) +{ + jbyte *lparg1=NULL; + jboolean rc = 0; + WebKitGTK_NATIVE_ENTER(env, that, _1webkit_1user_1content_1manager_1register_1script_1message_1handler_FUNC); + if (arg1) if ((lparg1 = (*env)->GetByteArrayElements(env, arg1, NULL)) == NULL) goto fail; +/* + rc = (jboolean)webkit_user_content_manager_register_script_message_handler(arg0, lparg1); +*/ + { + WebKitGTK_LOAD_FUNCTION(fp, webkit_user_content_manager_register_script_message_handler) + if (fp) { + rc = (jboolean)((jboolean (CALLING_CONVENTION*)(jintLong, jbyte *))fp)(arg0, lparg1); + } + } +fail: + if (arg1 && lparg1) (*env)->ReleaseByteArrayElements(env, arg1, lparg1, 0); + WebKitGTK_NATIVE_EXIT(env, that, _1webkit_1user_1content_1manager_1register_1script_1message_1handler_FUNC); + return rc; +} +#endif + #ifndef NO__1webkit_1web_1context_1get_1default JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1web_1context_1get_1default) (JNIEnv *env, jclass that) @@ -2806,6 +2890,26 @@ JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1web_1view_1new) } #endif +#ifndef NO__1webkit_1web_1view_1new_1with_1user_1content_1manager +JNIEXPORT jintLong JNICALL WebKitGTK_NATIVE(_1webkit_1web_1view_1new_1with_1user_1content_1manager) + (JNIEnv *env, jclass that, jintLong arg0) +{ + jintLong rc = 0; + WebKitGTK_NATIVE_ENTER(env, that, _1webkit_1web_1view_1new_1with_1user_1content_1manager_FUNC); +/* + rc = (jintLong)webkit_web_view_new_with_user_content_manager(arg0); +*/ + { + WebKitGTK_LOAD_FUNCTION(fp, webkit_web_view_new_with_user_content_manager) + if (fp) { + rc = (jintLong)((jintLong (CALLING_CONVENTION*)(jintLong))fp)(arg0); + } + } + WebKitGTK_NATIVE_EXIT(env, that, _1webkit_1web_1view_1new_1with_1user_1content_1manager_FUNC); + return rc; +} +#endif + #ifndef NO__1webkit_1web_1view_1reload JNIEXPORT void JNICALL WebKitGTK_NATIVE(_1webkit_1web_1view_1reload) (JNIEnv *env, jclass that, jintLong arg0) diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c index 0ec4066958..9b819cc129 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c +++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.c @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corporation and others. All rights reserved. + * Copyright (c) 2009, 2017 IBM Corporation and others. All rights reserved. * The contents of this file are made available under the terms * of the GNU Lesser General Public License (LGPL) Version 2.1 that * accompanies this distribution (lgpl-v21.txt). The LGPL is also @@ -110,6 +110,8 @@ char * WebKitGTK_nativeFunctionNames[] = { "_1webkit_1hit_1test_1result_1context_1is_1link", "_1webkit_1hit_1test_1result_1get_1link_1title", "_1webkit_1hit_1test_1result_1get_1link_1uri", + "_1webkit_1javascript_1result_1get_1global_1context", + "_1webkit_1javascript_1result_1get_1value", "_1webkit_1javascript_1result_1unref", "_1webkit_1major_1version", "_1webkit_1micro_1version", @@ -127,6 +129,8 @@ char * WebKitGTK_nativeFunctionNames[] = { "_1webkit_1uri_1request_1get_1uri", "_1webkit_1uri_1request_1new", "_1webkit_1uri_1response_1get_1mime_1type", + "_1webkit_1user_1content_1manager_1new", + "_1webkit_1user_1content_1manager_1register_1script_1message_1handler", "_1webkit_1web_1context_1get_1default", "_1webkit_1web_1context_1set_1favicon_1database_1directory", "_1webkit_1web_1data_1source_1get_1data", @@ -162,6 +166,7 @@ char * WebKitGTK_nativeFunctionNames[] = { "_1webkit_1web_1view_1load_1string", "_1webkit_1web_1view_1load_1uri", "_1webkit_1web_1view_1new", + "_1webkit_1web_1view_1new_1with_1user_1content_1manager", "_1webkit_1web_1view_1reload", "_1webkit_1web_1view_1run_1javascript", "_1webkit_1web_1view_1stop_1loading", diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h index 6b1491dd0f..36d3bf6b18 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h +++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/library/webkitgtk_stats.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2016 IBM Corporation and others. All rights reserved. + * Copyright (c) 2009, 2017 IBM Corporation and others. All rights reserved. * The contents of this file are made available under the terms * of the GNU Lesser General Public License (LGPL) Version 2.1 that * accompanies this distribution (lgpl-v21.txt). The LGPL is also @@ -120,6 +120,8 @@ typedef enum { _1webkit_1hit_1test_1result_1context_1is_1link_FUNC, _1webkit_1hit_1test_1result_1get_1link_1title_FUNC, _1webkit_1hit_1test_1result_1get_1link_1uri_FUNC, + _1webkit_1javascript_1result_1get_1global_1context_FUNC, + _1webkit_1javascript_1result_1get_1value_FUNC, _1webkit_1javascript_1result_1unref_FUNC, _1webkit_1major_1version_FUNC, _1webkit_1micro_1version_FUNC, @@ -137,6 +139,8 @@ typedef enum { _1webkit_1uri_1request_1get_1uri_FUNC, _1webkit_1uri_1request_1new_FUNC, _1webkit_1uri_1response_1get_1mime_1type_FUNC, + _1webkit_1user_1content_1manager_1new_FUNC, + _1webkit_1user_1content_1manager_1register_1script_1message_1handler_FUNC, _1webkit_1web_1context_1get_1default_FUNC, _1webkit_1web_1context_1set_1favicon_1database_1directory_FUNC, _1webkit_1web_1data_1source_1get_1data_FUNC, @@ -172,6 +176,7 @@ typedef enum { _1webkit_1web_1view_1load_1string_FUNC, _1webkit_1web_1view_1load_1uri_FUNC, _1webkit_1web_1view_1new_FUNC, + _1webkit_1web_1view_1new_1with_1user_1content_1manager_FUNC, _1webkit_1web_1view_1reload_FUNC, _1webkit_1web_1view_1run_1javascript_FUNC, _1webkit_1web_1view_1stop_1loading_FUNC, diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java index 79d740fbf3..25625a0384 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java +++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/browser/WebKit.java @@ -13,6 +13,7 @@ package org.eclipse.swt.browser; import java.io.*; +import java.lang.reflect.*; import java.net.*; import java.nio.charset.*; import java.util.*; @@ -32,10 +33,14 @@ class WebKit extends WebBrowser { String[] headers; boolean ignoreDispose, loadingText, untrustedText; byte[] htmlBytes; - BrowserFunction eventFunction; + BrowserFunction eventFunction; //Webkit1 only. static int DisabledJSCount; - static long /*int*/ ExternalClass, PostString, WebViewType; + + /** Webkit1 only. Used for callJava. See JSObjectHasPropertyProc */ + static long /*int*/ ExternalClass; + + static long /*int*/ PostString, WebViewType; static boolean IsWebKit14orNewer, LibraryLoaded; static Map<LONG, LONG> WindowMappings = new HashMap<> (); @@ -98,7 +103,23 @@ class WebKit extends WebBrowser { /* the following Callbacks are never freed */ static Callback Proc2, Proc3, Proc4, Proc5, Proc6; - static Callback JSObjectHasPropertyProc, JSObjectGetPropertyProc, JSObjectCallAsFunctionProc; + + + /** + * Webkit1 only: For javascript to call java via it's 'callJava'. + * For webkit2, see Webkit2JavaCallback. + * + * Webkit1: - callJava is implemented via an external object + * - Creates an object 'external' on javascipt side. + * -- see create(..) where it's initialized + * -- see webkit_window_object_cleared where it re-creates it on page-reloads + * - Javascript will call 'external.callJava' (where callJava is a property of 'external'). + * this triggers JSObjectGetPropertyProc(..) callback, which initializes callJava function. + * Then the external.callJava reaches JSObjectCallAsFunctionProc(..) and subsequently WebKit.java:callJava(..) is called. + */ + static Callback JSObjectHasPropertyProc, JSObjectGetPropertyProc, JSObjectCallAsFunctionProc; // webkit1 only. + + /** Webkit1 & Webkit2, Process key/mouse events from javascript. */ static Callback JSDOMEventProc; static boolean WEBKIT2; @@ -130,12 +151,19 @@ class WebKit extends WebBrowser { if (Proc5.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); Proc6 = new Callback (WebKit.class, "Proc", 6); //$NON-NLS-1$ if (Proc6.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); - JSObjectHasPropertyProc = new Callback (WebKit.class, "JSObjectHasPropertyProc", 3); //$NON-NLS-1$ - if (JSObjectHasPropertyProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); - JSObjectGetPropertyProc = new Callback (WebKit.class, "JSObjectGetPropertyProc", 4); //$NON-NLS-1$ - if (JSObjectGetPropertyProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); - JSObjectCallAsFunctionProc = new Callback (WebKit.class, "JSObjectCallAsFunctionProc", 6); //$NON-NLS-1$ - if (JSObjectCallAsFunctionProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + + + if (WEBKIT2) { + new Webkit2JavaCallback(); + } else { + JSObjectHasPropertyProc = new Callback (WebKit.class, "JSObjectHasPropertyProc", 3); //$NON-NLS-1$ + if (JSObjectHasPropertyProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + JSObjectGetPropertyProc = new Callback (WebKit.class, "JSObjectGetPropertyProc", 4); //$NON-NLS-1$ + if (JSObjectGetPropertyProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + JSObjectCallAsFunctionProc = new Callback (WebKit.class, "JSObjectCallAsFunctionProc", 6); //$NON-NLS-1$ + if (JSObjectCallAsFunctionProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + } + JSDOMEventProc = new Callback (WebKit.class, "JSDOMEventProc", 3); //$NON-NLS-1$ if (JSDOMEventProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); @@ -225,6 +253,104 @@ class WebKit extends WebBrowser { } /** + * For javascript to call java. + * This callback is special in that we link Javascript to a C function and a C function to + * the SWT java function. + * + * Note there is an architecture difference how callJava is implemented in Webkit1 vs Webkit2: + * + * Webkit1: See JSObjectHasPropertyProc. + * + * Webkit2: - callJava is implemented by connecting and calling a webkit signal: + * - webkit2JavaCallProc is linked from C to java. + * - Each webkit instance connects a signal (Webkit2JavaCallback.signal) to Webkit2JavaCallback.webkit2JavaCallProc + * via Webkit2JavaCallback.connectSignal(..) + * - (Note, webView is created with user_content_manager on webkit2.) + * - callJava is a wrapper that calls window.webkit.messageHandlers.webkit2JavaCallProc.postMessage([index,token, args]), + * which triggers the script-message-received::webkit2JavaCallProc signal and is forwarded to webkit2JavaCallProc. + **/ + static class Webkit2JavaCallback { + private static final String JavaScriptFunctionName = "webkit2JavaCallProc"; // $NON-NLS-1$ + private static final String Signal = "script-message-received::" + JavaScriptFunctionName; // $NON-NLS-1$ + + static final String JavaScriptFunctionDeclaration = + "if (!window.callJava) {\n" + + " window.callJava = function callJava(index, token, args) {\n" + + " window.webkit.messageHandlers." + JavaScriptFunctionName + ".postMessage([index,token, args]);\n" + + " }\n" + + "};\n"; + + private static Callback callback; + static { + callback = new Callback (Webkit2JavaCallback.class, JavaScriptFunctionName, void.class, new Type[] {long.class, long.class, long.class}); + if (callback.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); + } + + /** + * This method is called directly from javascript via something like: <br> + * window.webkit.messageHandlers.webkit2JavaCallProc.postMessage('helloWorld') <br> + * - Note, this method is async when called from javascript. <br> + * - This method name MUST match: this.JavaScriptFunctionName <br> + * - This method somewhat mirrors 'long callJava(ctx,func...)'. Except that it doesn't return a value. + * Docu: <br> + * https://webkitgtk.org/reference/webkit2gtk/stable/WebKitUserContentManager.html#WebKitUserContentManager-script-message-received + * */ + @SuppressWarnings("unused") // Method is called only directly from javascript. + private static void webkit2JavaCallProc (long /*int*/ WebKitUserContentManagerPtr, long /*int*/ WebKitJavascriptResultPtr, long /*int*/ webViewPtr) { + try { + long /*int*/ context = WebKitGTK.webkit_javascript_result_get_global_context (WebKitJavascriptResultPtr); + long /*int*/ value = WebKitGTK.webkit_javascript_result_get_value (WebKitJavascriptResultPtr); + Object[] arguments = (Object[]) convertToJava(context, value); + if (arguments.length != 3) throw new IllegalArgumentException("Expected 3 args. Received: " + arguments.length); + + Double index = (Double) arguments[0]; + String token = (String) arguments[1]; + + Browser browser = FindBrowser(webViewPtr); + if (browser == null) throw new NullPointerException("Could not find assosiated browser instance for handle: " + webViewPtr); + + BrowserFunction function = browser.webBrowser.functions.get(index.intValue()); + + if (function == null) throw new NullPointerException("Could not find function with index: " + index); + if (!token.equals(function.token)) throw new IllegalStateException("Function token missmatch. Expected:" + function.token + " actual:" + token); + if (! (arguments[2] instanceof Object[])) { + throw new IllegalArgumentException("Javascript did not provide any arguments. An empty callback [like call()] should still provide an empty array"); + } + + try { + // TODO someday : Support return values. See Bug 510905 + function.function ((Object[]) arguments[2]); + } catch (Exception e) { + // Exception in user function. + // Normally we would return an error to javascript. See callJava(..). + // But support for returning item back to java not implemented yet. + } + + } catch (RuntimeException e) { + System.err.println("\nSWT Webkit2 internal error: Javascript callback from Webkit to Java encountered an error while processing the callback:"); + System.err.println("Please report this via: https://bugs.eclipse.org/bugs/enter_bug.cgi?alias=&assigned_to=platform-swt-inbox%40eclipse.org&attach_text=&blocked=&bug_file_loc=http%3A%2F%2F&bug_severity=normal&bug_status=NEW&comment=&component=SWT&contenttypeentry=&contenttypemethod=autodetect&contenttypeselection=text%2Fplain&data=&defined_groups=1&dependson=&description=&flag_type-1=X&flag_type-11=X&flag_type-12=X&flag_type-13=X&flag_type-14=X&flag_type-15=X&flag_type-16=X&flag_type-2=X&flag_type-4=X&flag_type-6=X&flag_type-7=X&flag_type-8=X&form_name=enter_bug&keywords=&maketemplate=Remember%20values%20as%20bookmarkable%20template&op_sys=Linux&product=Platform&qa_contact=&rep_platform=PC&requestee_type-1=&requestee_type-2=&short_desc=&version=4.7"); + e.printStackTrace(); + } + return; + } + + /** Connect an instance of a webkit to the callback. */ + static void connectSignal(long /*int*/ WebKitUserContentManager, long /*int*/ webView) { + OS.g_signal_connect (WebKitUserContentManager, Converter.wcsToMbcs (Signal, true), callback.getAddress (), webView); + WebKitGTK.webkit_user_content_manager_register_script_message_handler(WebKitUserContentManager, Converter.wcsToMbcs(JavaScriptFunctionName, true)); + } + } + + @Override + String getJavaCallDeclaration() { + if (WEBKIT2) { + return Webkit2JavaCallback.JavaScriptFunctionDeclaration; + } else { + return super.getJavaCallDeclaration(); + } + } + + /** * Gets the webkit version, within an <code>int[3]</code> array with * <code>{major, minor, micro}</code> version */ @@ -273,7 +399,16 @@ static boolean IsInstalled () { (major == MIN_VERSION[0] && minor == MIN_VERSION[1] && micro >= MIN_VERSION[2]); } +/** + * Webkit1 callback. Used when external.callJava is called in javascript. + * Not used by Webkit2. + */ static long /*int*/ JSObjectCallAsFunctionProc (long /*int*/ ctx, long /*int*/ function, long /*int*/ thisObject, long /*int*/ argumentCount, long /*int*/ arguments, long /*int*/ exception) { + if (WEBKIT2) { + System.err.println("Internal error: SWT JSObjectCallAsFunctionProc. This should never have been called on webkit2."); + return 0; + } + if (WebKitGTK.JSValueIsObjectOfClass (ctx, thisObject, ExternalClass) == 0) { return WebKitGTK.JSValueMakeUndefined (ctx); } @@ -286,7 +421,16 @@ static long /*int*/ JSObjectCallAsFunctionProc (long /*int*/ ctx, long /*int*/ f return webkit.callJava (ctx, function, thisObject, argumentCount, arguments, exception); } +/** + * This callback is only being ran by webkit1. Only for 'callJava'. + * It's used to initialize the 'callJava' function pointer in the 'external' object, + * such that external.callJava reaches Java land. + */ static long /*int*/ JSObjectGetPropertyProc (long /*int*/ ctx, long /*int*/ object, long /*int*/ propertyName, long /*int*/ exception) { + if (WEBKIT2) { + System.err.println("Internal error: SWT WebKit.java:JSObjectGetPropertyProc. This should never have been called on webkit2."); + return 0; + } byte[] bytes = (FUNCTIONNAME_CALLJAVA + '\0').getBytes (StandardCharsets.UTF_8); //$NON-NLS-1$ long /*int*/ name = WebKitGTK.JSStringCreateWithUTF8CString (bytes); long /*int*/ function = WebKitGTK.JSObjectMakeFunctionWithCallback (ctx, name, JSObjectCallAsFunctionProc.getAddress ()); @@ -294,7 +438,14 @@ static long /*int*/ JSObjectGetPropertyProc (long /*int*/ ctx, long /*int*/ obje return function; } +/** + * Webkit1: Check if the 'external' object regiseterd earlied has the 'callJava' property. + */ static long /*int*/ JSObjectHasPropertyProc (long /*int*/ ctx, long /*int*/ object, long /*int*/ propertyName) { + if (WEBKIT2) { + System.err.println("Internal error: SWT JSObjectHasPropertyProc. This should never have been called on webkit2."); + return 0; + } byte[] bytes = (FUNCTIONNAME_CALLJAVA + '\0').getBytes (StandardCharsets.UTF_8); //$NON-NLS-1$ return WebKitGTK.JSStringIsEqualToUTF8CString (propertyName, bytes); } @@ -558,17 +709,22 @@ public void create (Composite parent, int style) { if (Device.DEBUG) { System.out.println(String.format("WebKit version %s.%s.%s", vers[0], vers[1], vers[2])); //$NON-NLS-1$ } - JSClassDefinition jsClassDefinition = new JSClassDefinition (); - byte[] bytes = Converter.wcsToMbcs (CLASSNAME_EXTERNAL, true); - jsClassDefinition.className = C.malloc (bytes.length); - OS.memmove (jsClassDefinition.className, bytes, bytes.length); - jsClassDefinition.hasProperty = JSObjectHasPropertyProc.getAddress (); - jsClassDefinition.getProperty = JSObjectGetPropertyProc.getAddress (); - long /*int*/ classDefinitionPtr = C.malloc (JSClassDefinition.sizeof); - WebKitGTK.memmove (classDefinitionPtr, jsClassDefinition, JSClassDefinition.sizeof); - ExternalClass = WebKitGTK.JSClassCreate (classDefinitionPtr); - - bytes = Converter.wcsToMbcs ("POST", true); //$NON-NLS-1$ + + if (!WEBKIT2) { // 'external' object only used on webkit1 for javaCall. Webkit2 has a different mechanism. + JSClassDefinition jsClassDefinition = new JSClassDefinition (); + byte[] bytes = Converter.wcsToMbcs (CLASSNAME_EXTERNAL, true); + jsClassDefinition.className = C.malloc (bytes.length); + OS.memmove (jsClassDefinition.className, bytes, bytes.length); + + jsClassDefinition.hasProperty = JSObjectHasPropertyProc.getAddress (); + jsClassDefinition.getProperty = JSObjectGetPropertyProc.getAddress (); + long /*int*/ classDefinitionPtr = C.malloc (JSClassDefinition.sizeof); + WebKitGTK.memmove (classDefinitionPtr, jsClassDefinition, JSClassDefinition.sizeof); + + ExternalClass = WebKitGTK.JSClassCreate (classDefinitionPtr); + } + + byte [] bytes = Converter.wcsToMbcs ("POST", true); //$NON-NLS-1$ PostString = C.malloc (bytes.length); C.memmove (PostString, bytes, bytes.length); @@ -593,7 +749,15 @@ public void create (Composite parent, int style) { OS.gtk_scrolled_window_set_policy (scrolledWindow, OS.GTK_POLICY_AUTOMATIC, OS.GTK_POLICY_AUTOMATIC); } - webView = WebKitGTK.webkit_web_view_new (); + if (WEBKIT2) { + // On Webkit2, webView has to be created with UserContentManager so that Javascript callbacks work. See #508217 + long /*int*/ WebKitUserContentManager = WebKitGTK.webkit_user_content_manager_new(); + webView = WebKitGTK.webkit_web_view_new_with_user_content_manager (WebKitUserContentManager); + Webkit2JavaCallback.connectSignal(WebKitUserContentManager, webView); + } else { // Webkit1 + webView = WebKitGTK.webkit_web_view_new (); + } + webViewData = C.malloc (C.PTR_SIZEOF); C.memmove (webViewData, new long /*int*/[] {webView}, C.PTR_SIZEOF); @@ -746,12 +910,14 @@ public void create (Composite parent, int style) { } } - eventFunction = new BrowserFunction (browser, "HandleWebKitEvent") { //$NON-NLS-1$ - @Override - public Object function(Object[] arguments) { - return handleEventFromFunction (arguments) ? Boolean.TRUE : Boolean.FALSE; - } - }; + if (!WEBKIT2) { // HandleWebKitEvent registration. Pre Webkit 1.4 way of handling mouse/keyboard events. Webkit2 uses dom. + eventFunction = new BrowserFunction (browser, "HandleWebKitEvent") { //$NON-NLS-1$ + @Override + public Object function(Object[] arguments) { + return handleEventFromFunction (arguments) ? Boolean.TRUE : Boolean.FALSE; + } + }; + } /* * Bug in WebKitGTK. MouseOver/MouseLeave events are not consistently sent from @@ -812,51 +978,54 @@ void addEventHandlers (long /*int*/ web_view, boolean top) { return; } - /* install the JS call-out to the registered BrowserFunction */ - StringBuffer buffer = new StringBuffer ("window.SWTkeyhandler = function SWTkeyhandler(e) {"); //$NON-NLS-1$ - buffer.append ("try {e.returnValue = HandleWebKitEvent(e.type, e.keyCode, e.charCode, e.altKey, e.ctrlKey, e.shiftKey, e.metaKey);} catch (e) {}};"); //$NON-NLS-1$ - execute (buffer.toString ()); - buffer = new StringBuffer ("window.SWTmousehandler = function SWTmousehandler(e) {"); //$NON-NLS-1$ - buffer.append ("try {e.returnValue = HandleWebKitEvent(e.type, e.screenX, e.screenY, e.detail, e.button, e.altKey, e.ctrlKey, e.shiftKey, e.metaKey, e.relatedTarget != null);} catch (e) {}};"); //$NON-NLS-1$ - execute (buffer.toString ()); - if (top) { - /* DOM API is not available, so add listener to top-level document */ - buffer = new StringBuffer ("document.addEventListener('keydown', SWTkeyhandler, true);"); //$NON-NLS-1$ - buffer.append ("document.addEventListener('keypress', SWTkeyhandler, true);"); //$NON-NLS-1$ - buffer.append ("document.addEventListener('keyup', SWTkeyhandler, true);"); //$NON-NLS-1$ - buffer.append ("document.addEventListener('mousedown', SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("document.addEventListener('mouseup', SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("document.addEventListener('mousemove', SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("document.addEventListener('mousewheel', SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("document.addEventListener('dragstart', SWTmousehandler, true);"); //$NON-NLS-1$ + if (!WEBKIT2) { // add HandleWebKitEvent key/mouse handlers + /* install the JS call-out to the registered BrowserFunction */ + StringBuffer buffer = new StringBuffer ("window.SWTkeyhandler = function SWTkeyhandler(e) {"); //$NON-NLS-1$ + buffer.append ("try {e.returnValue = HandleWebKitEvent(e.type, e.keyCode, e.charCode, e.altKey, e.ctrlKey, e.shiftKey, e.metaKey);} catch (e) {}};"); //$NON-NLS-1$ + execute (buffer.toString ()); + buffer = new StringBuffer ("window.SWTmousehandler = function SWTmousehandler(e) {"); //$NON-NLS-1$ + buffer.append ("try {e.returnValue = HandleWebKitEvent(e.type, e.screenX, e.screenY, e.detail, e.button, e.altKey, e.ctrlKey, e.shiftKey, e.metaKey, e.relatedTarget != null);} catch (e) {}};"); //$NON-NLS-1$ + execute (buffer.toString ()); - /* - * The following two lines are intentionally commented because they cannot be used to - * consistently send MouseEnter/MouseExit events until https://bugs.webkit.org/show_bug.cgi?id=35246 - * is fixed. - */ - //buffer.append ("document.addEventListener('mouseover', SWTmousehandler, true);"); //$NON-NLS-1$ - //buffer.append ("document.addEventListener('mouseout', SWTmousehandler, true);"); //$NON-NLS-1$ + if (top) { + /* DOM API is not available, so add listener to top-level document */ + buffer = new StringBuffer ("document.addEventListener('keydown', SWTkeyhandler, true);"); //$NON-NLS-1$ + buffer.append ("document.addEventListener('keypress', SWTkeyhandler, true);"); //$NON-NLS-1$ + buffer.append ("document.addEventListener('keyup', SWTkeyhandler, true);"); //$NON-NLS-1$ + buffer.append ("document.addEventListener('mousedown', SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("document.addEventListener('mouseup', SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("document.addEventListener('mousemove', SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("document.addEventListener('mousewheel', SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("document.addEventListener('dragstart', SWTmousehandler, true);"); //$NON-NLS-1$ + /* + * The following two lines are intentionally commented because they cannot be used to + * consistently send MouseEnter/MouseExit events until https://bugs.webkit.org/show_bug.cgi?id=35246 + * is fixed. + */ + //buffer.append ("document.addEventListener('mouseover', SWTmousehandler, true);"); //$NON-NLS-1$ + //buffer.append ("document.addEventListener('mouseout', SWTmousehandler, true);"); //$NON-NLS-1$ + + execute (buffer.toString ()); + return; + } + + /* add JS event listener in frames */ + buffer = new StringBuffer ("for (var i = 0; i < frames.length; i++) {"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('keydown', window.SWTkeyhandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('keypress', window.SWTkeyhandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('keyup', window.SWTkeyhandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('mousedown', window.SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('mouseup', window.SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('mousemove', window.SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('mouseover', window.SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('mouseout', window.SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('mousewheel', window.SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ("frames[i].document.addEventListener('dragstart', window.SWTmousehandler, true);"); //$NON-NLS-1$ + buffer.append ('}'); execute (buffer.toString ()); - return; } - - /* add JS event listener in frames */ - buffer = new StringBuffer ("for (var i = 0; i < frames.length; i++) {"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('keydown', window.SWTkeyhandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('keypress', window.SWTkeyhandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('keyup', window.SWTkeyhandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('mousedown', window.SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('mouseup', window.SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('mousemove', window.SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('mouseover', window.SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('mouseout', window.SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('mousewheel', window.SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ("frames[i].document.addEventListener('dragstart', window.SWTmousehandler, true);"); //$NON-NLS-1$ - buffer.append ('}'); - execute (buffer.toString ()); } @Override @@ -940,15 +1109,6 @@ public boolean execute (String script) { @Override public Object evaluate (String script) throws SWTException { if (WEBKIT2){ - - if (script.contains(FUNCTIONNAME_CALLJAVA)) { - // Bug 508217 - Support for browser.funcction/close not yet implemented. - // trying to execute 'callJava' code currently can lead to infinite - // recursion/loops causing freeze ups. See Bug 510183. - // Disabling till function and close() implemented. - return null; - } - /* Webkit2: We remove the 'return' prefix that normally comes with the script. * The reason is that in Webkit1, script was wrapped into a function and if an exception occured * it was caught on Javascript side and a callback to java was made. @@ -2066,6 +2226,10 @@ long /*int*/ webkit_notify_load_status (long /*int*/ web_view, long /*int*/ pspe return 0; } +/** + * This method is only called by Webkit2. + * The webkit1 equivalent is webkit_window_object_cleared; + */ long /*int*/ webkit_load_changed (long /*int*/ web_view, int status, long user_data) { switch (status) { case WebKitGTK.WEBKIT2_LOAD_COMMITTED: { @@ -2073,6 +2237,10 @@ long /*int*/ webkit_load_changed (long /*int*/ web_view, int status, long user_d return handleLoadCommitted (uri, true); } case WebKitGTK.WEBKIT2_LOAD_FINISHED: { + + registerBrowserFunctions(); // Bug 508217 + addEventHandlers (web_view, true); + long /*int*/ title = WebKitGTK.webkit_web_view_get_title (webView); if (title == 0) { long /*int*/ uri = WebKitGTK.webkit_web_view_get_uri (webView); @@ -2318,6 +2486,10 @@ long /*int*/ webkit_web_view_ready (long /*int*/ web_view) { return 0; } +/** + * This method is only called by Webkit1. + * The webkit2 equivalent is webkit_load_changed(..):caseWEBKIT2__LOAD_FINISHED + */ long /*int*/ webkit_window_object_cleared (long /*int*/ web_view, long /*int*/ frame, long /*int*/ context, long /*int*/ window_object) { long /*int*/ globalObject = WebKitGTK.JSContextGetGlobalObject (context); long /*int*/ externalObject = WebKitGTK.JSObjectMake (context, ExternalClass, webViewData); @@ -2325,17 +2497,25 @@ long /*int*/ webkit_window_object_cleared (long /*int*/ web_view, long /*int*/ f long /*int*/ name = WebKitGTK.JSStringCreateWithUTF8CString (bytes); WebKitGTK.JSObjectSetProperty (context, globalObject, name, externalObject, 0, null); WebKitGTK.JSStringRelease (name); + + registerBrowserFunctions(); // Bug 508217 + long /*int*/ mainFrame = WebKitGTK.webkit_web_view_get_main_frame (webView); + boolean top = mainFrame == frame; + addEventHandlers (web_view, top); + return 0; +} + +private void registerBrowserFunctions() { Iterator<BrowserFunction> elements = functions.values().iterator (); while (elements.hasNext ()) { BrowserFunction current = elements.next (); execute (current.functionString); } - long /*int*/ mainFrame = WebKitGTK.webkit_web_view_get_main_frame (webView); - boolean top = mainFrame == frame; - addEventHandlers (web_view, top); - return 0; } +/** + * Webkit1 callback for javascript to call java. + */ long /*int*/ callJava (long /*int*/ ctx, long /*int*/ func, long /*int*/ thisObject, long /*int*/ argumentCount, long /*int*/ arguments, long /*int*/ exception) { Object returnValue = null; if (argumentCount == 3) { @@ -2415,7 +2595,7 @@ long /*int*/ convertToJS (long /*int*/ ctx, Object value) { return 0; } -Object convertToJava (long /*int*/ ctx, long /*int*/ value) { +static Object convertToJava (long /*int*/ ctx, long /*int*/ value) { int type = WebKitGTK.JSValueGetType (ctx, value); switch (type) { case WebKitGTK.kJSTypeBoolean: { diff --git a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java index 6a429efdb1..16fe5a7a93 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java +++ b/bundles/org.eclipse.swt/Eclipse SWT WebKit/gtk/org/eclipse/swt/internal/webkit/WebKitGTK.java @@ -46,6 +46,7 @@ public class WebKitGTK extends C { public static final int WEBKIT_CREDENTIAL_PERSISTENCE_FOR_SESSION = 1; public static final int WEBKIT_CREDENTIAL_PERSISTENCE_PERMANENT = 2; + /** Signals */ public static final byte[] authenticate = ascii ("authenticate"); // $NON-NLS-1$ public static final byte[] close_web_view = ascii ("close-web-view"); // $NON-NLS-1$ @@ -58,6 +59,7 @@ public class WebKitGTK extends C { public static final byte[] download_requested = ascii ("download-requested"); // $NON-NLS-1$ public static final byte[] download_started = ascii ("download-started"); // $NON-NLS-1$ public static final byte[] hovering_over_link = ascii ("hovering-over-link"); // $NON-NLS-1$ + /** Webkit2 only, to implement equivalent of webkit1 window_object_cleared*/ public static final byte[] load_changed = ascii ("load-changed"); // $NON-NLS-1$ public static final byte[] mime_type_policy_decision_requested = ascii ("mime-type-policy-decision-requested"); // $NON-NLS-1$ public static final byte[] mouse_target_changed = ascii ("mouse-target-changed"); // $NON-NLS-1$ @@ -74,8 +76,10 @@ public class WebKitGTK extends C { public static final byte[] status_bar_text_changed = ascii ("status-bar-text-changed"); // $NON-NLS-1$ public static final byte[] web_view_ready = ascii ("web-view-ready"); // $NON-NLS-1$ public static final byte[] ready_to_show = ascii ("ready-to-show"); // $NON-NLS-1$ + /** Webkit1 only. On Webkit2 this is found in a webextension. Instead 'load_changed' is used on webkit2 **/ public static final byte[] window_object_cleared = ascii ("window-object-cleared"); // $NON-NLS-1$ + /** Properties */ public static final byte[] default_encoding = ascii ("default-encoding"); // $NON-NLS-1$ public static final byte[] default_charset = ascii ("default-charset"); // $NON-NLS-1$ @@ -1523,6 +1527,69 @@ public static final long /*int*/ webkit_web_view_new () { } /** @method flags=dynamic */ +public static final native long /*int*/ _webkit_user_content_manager_new(); +public static final long /*int*/ webkit_user_content_manager_new() { + lock.lock(); + try { + return _webkit_user_content_manager_new (); + } finally { + lock.unlock(); + } +} + +/** + * @method flags=dynamic + * @param js_result cast=(gpointer) + */ +public static final native long /*int*/ _webkit_javascript_result_get_global_context(long /*int*/ js_result); +/** JSGlobalContextRef webkit_javascript_result_get_global_context (WebKitJavascriptResult *js_result); */ +public static final long /*int*/ webkit_javascript_result_get_global_context(long /*int*/ js_result) { + lock.lock(); + try { + return _webkit_javascript_result_get_global_context (js_result); + } finally { + lock.unlock(); + } +} + +/** + * @method flags=dynamic + * @param js_result cast=(gpointer) + */ +public static final native long /*int*/ _webkit_javascript_result_get_value(long /*int*/ js_result); +/** JSValueRef webkit_javascript_result_get_value (WebKitJavascriptResult *js_result); */ +public static final long /*int*/ webkit_javascript_result_get_value(long /*int*/ js_result) { + lock.lock(); + try { + return _webkit_javascript_result_get_value (js_result); + } finally { + lock.unlock(); + } +} + +/** @method flags=dynamic */ +public static final native boolean _webkit_user_content_manager_register_script_message_handler(long /*int*/ WebKitUserContentManager, byte[] name); +public static final boolean webkit_user_content_manager_register_script_message_handler(long /*int*/ WebKitUserContentManager, byte[] name) { + lock.lock(); + try { + return _webkit_user_content_manager_register_script_message_handler (WebKitUserContentManager, name); + } finally { + lock.unlock(); + } +} + +/** @method flags=dynamic */ +public static final native long /*int*/ _webkit_web_view_new_with_user_content_manager (long /*int*/ WebKitUserContentManager); +public static final long /*int*/ webkit_web_view_new_with_user_content_manager (long /*int*/ WebKitUserContentManager) { + lock.lock(); + try { + return _webkit_web_view_new_with_user_content_manager (WebKitUserContentManager); + } finally { + lock.unlock(); + } +} + +/** @method flags=dynamic */ public static final native void _webkit_web_view_reload (long /*int*/ web_view); public static final void webkit_web_view_reload (long /*int*/ web_view) { lock.lock(); diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java index 87eaea190a..0d4c31549f 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java @@ -63,6 +63,7 @@ public class Test_org_eclipse_swt_browser_Browser extends Test_org_eclipse_swt_w boolean browser_debug = false; boolean isWebkit1 = false; + boolean isWebkit2 = false; /** * Normally, sleep in 1 ms intervals 1000 times. During browser_debug, sleep 1000 ms for 1 interval. @@ -106,6 +107,8 @@ public void setUp() { // webkitgtk 2.5 and onwards uses webkit2. if (webkitGtkVersionInts[0] == 1 || (webkitGtkVersionInts[0] == 2 && webkitGtkVersionInts[1] <= 4)) { isWebkit1 = true; + } else if (webkitGtkVersionInts[0] == 2 && webkitGtkVersionInts[1] > 4) { + isWebkit2 = true; } } shell.setText(shellTitle); @@ -785,7 +788,9 @@ public void test_execute_and_closeListener () { */ @Test public void test_evaluate_string() { - assumeFalse(webkit1SkipMsg(), isWebkit1); // Bug 509411 + // Run locally, skip on hudson. see Bug 509411 + // This test sometimes crashes on webkit1, but it's useful to test at least one 'evaluate' situation. + assumeFalse(webkit1SkipMsg(), (SwtTestUtil.isRunningOnEclipseOrgHudsonGTK && isWebkit1)); final AtomicReference<String> returnValue = new AtomicReference<>(); browser.addProgressListener(new ProgressListener() { @@ -1227,7 +1232,8 @@ public void test_BrowserFunction_callback_with_integer () { // On webkit1, this test works if ran on it's own. But sometimes in test-suite with other tests it causes jvm crash. // culprit seems to be the main_context_iteration() call in shell.setVisible(). // See Bug 509587. Solution: Webkit2. - assumeFalse(webkit1SkipMsg(), isWebkit1); + // It's useful to run at least one function test on webkit1 locally. + assumeFalse(webkit1SkipMsg(), (SwtTestUtil.isRunningOnEclipseOrgHudsonGTK && isWebkit1)); // run locally. Skip on hudson that runs webkit1. AtomicInteger returnInt = new AtomicInteger(0); @@ -1459,6 +1465,9 @@ public void test_BrowserFunction_callback_with_javaReturningInt () { // See Bug 509587. Solution: Webkit2. assumeFalse(webkit1SkipMsg(), isWebkit1); + // Skip till Bug 510905 is implemented. + assumeFalse("Skipping test_BrowserFunction_callback_with_javaReturningInt. Java's callback to Javascript doesn't support return yet", isWebkit2); + AtomicInteger returnInt = new AtomicInteger(0); class JavascriptCallback extends BrowserFunction { // Note: Local class defined inside method. @@ -1515,6 +1524,71 @@ public void test_BrowserFunction_callback_with_javaReturningInt () { } +/** + * Test that a callback works even after a new page is loaded. + * I.e, BrowserFunctions should have to be re-initialized after a page load. + * + * Logic: + * - load a page. + * - Register java callback. + * - call java callback from javascript. (exec) + * + * - java callback instantiates new page load. + * - new page load triggers 'completed' listener + * - completed listener calls the registered function again. + * + * - once regiseterd function is called a 2nd time, it sets the test to pass. + */ +@Test +public void test_BrowserFunction_callback_afterPageReload() { + // On webkit1, this test works if ran on it's own. But sometimes in test-suite with other tests it causes jvm crash. + // culprit seems to be the main_context_iteration() call in shell.setVisible(). + // See Bug 509587. Solution: Webkit2. + assumeFalse(webkit1SkipMsg(), isWebkit1); + + AtomicBoolean javaCallbackExecuted = new AtomicBoolean(false); + AtomicInteger callCount = new AtomicInteger(0); + + class JavascriptCallback extends BrowserFunction { // Note: Local class defined inside method. + JavascriptCallback(Browser browser, String name) { + super(browser, name); + } + + @Override + public Object function(Object[] arguments) { + if (callCount.get() == 0) { + callCount.set(1); + browser.setText("2nd page load"); + } else { + javaCallbackExecuted.set(true); + } + return null; + } + } + browser.setText("1st (initial) page load"); + new JavascriptCallback(browser, "jsCallbackToJava"); + browser.execute("jsCallbackToJava()"); + + browser.addProgressListener(new ProgressListener() { + @Override + public void completed(ProgressEvent event) { + // see if function still works after a page change: + browser.execute("jsCallbackToJava()"); + } + @Override + public void changed(ProgressEvent event) {} + }); + + shell.open(); + for (int i = 0; i < (loopMultipier * secondsToWaitTillFail); i++) { // Wait up to seconds before declaring test as failed. + runLoopTimer(waitMS); + if (javaCallbackExecuted.get()) { + return; // pass. + } + } + fail(); +} + /* custom */ void runLoopTimer(final int milliseconds) { |
