diff -r e8beeeaa062e modules/graphics/src/dalvik/java/com/oracle/dalvik/InternalWebView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/graphics/src/dalvik/java/com/oracle/dalvik/InternalWebView.java Fri May 08 22:20:14 2015 +0200 @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.dalvik; + +import android.graphics.Bitmap; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.JavascriptInterface; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import javafxports.android.*; + +public class InternalWebView { + + public final static int PAGE_STARTED = 0; + public final static int PAGE_FINISHED = 1; + public final static int PAGE_REDIRECTED = 2; + public final static int LOAD_FAILED = 5; + public final static int LOAD_STOPPED = 6; + public final static int CONTENT_RECEIVED = 10; + public final static int TITLE_RECEIVED = 11; + public final static int ICON_RECEIVED = 12; + public final static int CONTENTTYPE_RECEIVED = 13; + public final static int DOCUMENT_AVAILABLE = 14; + public final static int RESOURCE_STARTED = 20; + public final static int RESOURCE_REDIRECTED = 21; + public final static int RESOURCE_FINISHED = 22; + public final static int RESOURCE_FAILED = 23; + public final static int PROGRESS_CHANGED = 30; + private static final String TAG = "InternalWebView"; + private static List views = new ArrayList(); + private static int idcounter = 0; + private boolean isLayedOut = false; + private boolean initialized = false; + private int internalID; + private int x, y, width, height; + private WebView nativeWebView; + private String url, content; + private String contentType = "text/html"; + private String encoding = null; + private String htmlContent; + private boolean visible; + private boolean pageFinished = false; + + public InternalWebView() { + this.internalID = ++idcounter; + views.add(0, this); + } + + public int getInternalID() { + return this.internalID; + } + + + private void initialize() { + nativeWebView = new WebView(FXActivity.getInstance()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(width, height); + } + }; + nativeWebView.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + pageFinished = false; + fireLoadEvent(0, PAGE_STARTED, url, contentType, -1, -1); + } + + @Override + public void onPageFinished(WebView view, String url) { + if (!pageFinished) { + nativeWebView.loadUrl("javascript:window.HTMLOUT.processHTML(''+document.getElementsByTagName('html')[0].innerHTML+'');"); + } + pageFinished = true; + fireLoadEvent(0, PAGE_FINISHED, url, contentType, -1, -1); + } + + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + fireLoadEvent(0, LOAD_FAILED, failingUrl, contentType, -1, errorCode); + } + }); + + nativeWebView.setWebChromeClient(new WebChromeClient() { + @Override + public void onProgressChanged(WebView view, int newProgress) { + fireLoadEvent(0, PROGRESS_CHANGED, url, contentType, newProgress, -1); + } + }); + + WebSettings settings = nativeWebView.getSettings(); + settings.setSupportZoom(true); + settings.setJavaScriptEnabled(true); + nativeWebView.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT"); + initialized = true; + } + + public String getHtmlContent() { + return htmlContent; + } + + private void fireLoadEvent(int frameID, int state, String url, + String content_type, int progress, int errorCode) { + _fireLoadEvent(this.internalID, frameID, state, + url == null ? "" : url, + content_type == null ? "" : content_type, + progress, errorCode); + } + + private static int indexOf(long id) { + int i = 0; + for (InternalWebView wvp : views) { + if (id == wvp.internalID) { + return i; + } + i++; + } + return -1; + } + + private static InternalWebView getViewByID(int id) { + for (InternalWebView wvp : views) { + if (id == wvp.internalID) { + return wvp; + } + } + throw new RuntimeException("No InternalWebView with id: " + id); + } + + static void createNew() { + FXActivity.getInstance().runOnUiThread(new Runnable() { + public void run() { + new InternalWebView().getInternalID(); + } + }); + } + + public static void loadUrl(int id, String url) { + final InternalWebView iwv = InternalWebView.getViewByID(id); + iwv.setContent(null, null); + iwv.setUrl(url); + if (iwv.initialized && iwv.isLayedOut) { + FXActivity.getInstance().runOnUiThread(new Runnable() { + public void run() { + int c = FXActivity.getViewGroup().getChildCount(); + iwv.nativeWebView.loadUrl(iwv.url); + } + }); + } + } + + public static String getHtmlContent (int id) { + final InternalWebView iwv = InternalWebView.getViewByID(id); + return iwv.getHtmlContent(); + } + + public static void loadContent(int id, String content, String contentType) { + final InternalWebView iwv = InternalWebView.getViewByID(id); + iwv.setUrl(null); + iwv.setContent(content, contentType); + if (iwv.initialized && iwv.isLayedOut) { + FXActivity.getInstance().runOnUiThread(new Runnable() { + public void run() { + iwv.nativeWebView.loadData(iwv.content, iwv.contentType, iwv.encoding); + } + }); + } + } + + static void setEncoding(int id, String encoding) { + final InternalWebView iwv = InternalWebView.getViewByID(id); + iwv.setEncoding(encoding); + } + + public static void moveAndResize(int id, int x, int y, final int w, final int h) { + final boolean move; + final boolean resize; + + if (w == 0 || h == 0) { + return; + } + + final InternalWebView iwv = InternalWebView.getViewByID(id); + if (iwv == null) { + return; + } + if (iwv.x == x + && iwv.y == y + && iwv.width == w + && iwv.height == h) { + return; + } + + move = (iwv.x != x || iwv.y != y); + if (move) { + iwv.x = x; + iwv.y = y; + } + resize = (iwv.width != w || iwv.height != h); + if (resize) { + iwv.width = w; + iwv.height = h; + } + if (!iwv.visible) { + return; + } + + if (!iwv.isLayedOut) { + iwv.isLayedOut = true; + FXActivity.getInstance().runOnUiThread(new Runnable() { + public void run() { + if (!iwv.initialized) { + iwv.initialize(); + } + FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.NO_GRAVITY); + layout.leftMargin = iwv.x; + layout.topMargin = iwv.y; +// iwv.nativeWebView.setTranslationX(iwv.x); +// iwv.nativeWebView.setTranslationY(iwv.y); + FXActivity.getViewGroup().addView(iwv.nativeWebView, layout); + Log.v(TAG, String.format("WebView added to ViewGroup [x: %d, y: %d , w: %d h: %d]", + iwv.x, iwv.y, iwv.width, iwv.height)); + if (iwv.contentType == null || iwv.contentType.length() == 0) { + iwv.contentType = "text/html"; + } + if (iwv.url != null && iwv.url.length() > 0) { + Log.v(TAG, "Loading url: " + iwv.url); + iwv.nativeWebView.loadUrl(iwv.url); + } else if (iwv.content != null) { + Log.v(TAG, String.format("Loading content: %s\ncontent type: %s\nencoding: %s", + iwv.content, iwv.contentType, iwv.encoding)); + iwv.nativeWebView.loadData(iwv.content, iwv.contentType, iwv.encoding); + } + } + }); + + }// end of not initialized + else { + FXActivity.getInstance().runOnUiThread(new Runnable() { + public void run() { + if (move) { + FrameLayout.LayoutParams layout = + (FrameLayout.LayoutParams) iwv.nativeWebView.getLayoutParams(); + layout.leftMargin = iwv.x; + layout.topMargin = iwv.y; + FXActivity.getViewGroup().updateViewLayout(iwv.nativeWebView, layout); +// iwv.nativeWebView.setTranslationX(iwv.x); +// iwv.nativeWebView.setTranslationY(iwv.y); + } + if (move || resize) { + iwv.nativeWebView.invalidate(); + } + } + }); + } + } + + public static void setVisible(int id, final boolean visible) { + final InternalWebView iwv = InternalWebView.getViewByID(id); + if (iwv == null) { + return; + } + if (!iwv.initialized) { + iwv.visible = visible; + return; + } + FXActivity.getInstance().runOnUiThread(new Runnable() { + public void run() { + iwv.nativeWebView.setVisibility(visible ? View.VISIBLE : View.GONE); + if (visible) { + iwv.nativeWebView.invalidate(); + } + } + }); + } + + public static void dispose(int id) { + final InternalWebView iwv = InternalWebView.getViewByID(id); + InternalWebView.setVisible(id, false); + + FXActivity.getInstance().runOnUiThread(new Runnable() { + public void run() { + iwv.nativeWebView.stopLoading(); + iwv.nativeWebView.destroy(); + } + }); + views.remove(iwv); + } + + private void setUrl(String url) { + this.url = url; + } + + private void setContent(String content, String contentType) { + this.content = content; + this.contentType = contentType; + } + + private void setEncoding(String encoding) { + this.encoding = encoding; + } + + // private native void _fireLoadEvent(int id, int frameID, int state, String url, + private void _fireLoadEvent(int id, int frameID, int state, String url, + String contentType, int progress, int errorCode) { + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class clazz = Class.forName("com.sun.webkit.NativeWebView", true, cl); + Method m = clazz.getMethod("fire_load_event", int.class, int.class, int.class, + String.class, String.class, int.class, int.class); + m.invoke(null, id, frameID, state, url, contentType, progress, errorCode); + } + catch (Exception e) { + System.out.println ("[JVDBG] Error firing event"); + e.printStackTrace(); + } + } + + class MyJavaScriptInterface { + @JavascriptInterface + @SuppressWarnings("unused") + public void processHTML(String html) { + htmlContent = html; + fireLoadEvent(0, DOCUMENT_AVAILABLE, url, contentType, -1, -1); + } + } + + +} diff -r e8beeeaa062e modules/web/src/android/java/com/sun/webkit/NativeWebView.java --- a/modules/web/src/android/java/com/sun/webkit/NativeWebView.java Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/android/java/com/sun/webkit/NativeWebView.java Fri May 08 22:20:14 2015 +0200 @@ -27,8 +27,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.HashMap; +import java.util.Map; +import com.oracle.dalvik.InternalWebView; -class NativeWebView { +public class NativeWebView { private static List views = new ArrayList(); private int id; @@ -69,6 +72,10 @@ views.remove(this); } + String getHtmlContent() { + return InternalWebView.getHtmlContent(this.id); + } + private static NativeWebView getViewByID(int id) { for (NativeWebView wvp : views) { if (id == wvp.id) { @@ -79,7 +86,7 @@ return null; } - static void fire_load_event(final int id, final int frameID, final int state, + public static void fire_load_event(final int id, final int frameID, final int state, final String url, final String contenType, final int progress, final int errorCode) { final NativeWebView nwv = NativeWebView.getViewByID(id); @@ -95,19 +102,48 @@ }); } - private native void _moveAndResize(int id, int x, int y, int width, int height); + static Map webViews = new HashMap<>(); - private native void _setVisible(int id, boolean visible); + // private native void _moveAndResize(int id, int x, int y, int width, int height); + private void _moveAndResize(int id, int x, int y, int width, int height) { + InternalWebView.moveAndResize(id, x, y, width, height); + } - private native int _createAndroidWebView(); + // private native void _setVisible(int id, boolean visible); + private void _setVisible(int id, boolean visible) { + InternalWebView.setVisible(id, visible); + } - private native void _moveToTop(int id); + // private native int _createAndroidWebView(); + private int _createAndroidWebView() { + InternalWebView internalWebView = new InternalWebView(); + int id = internalWebView.getInternalID(); + webViews.put(id, internalWebView); + return id; + } - private native void _loadUrl(int id, String url); - private native void _dispose(int id); + // private native void _moveToTop(int id); + private void _moveToTop(int id) { + } - private native void _loadContent(int id, String content, String contentType); + // private native void _loadUrl(int id, String url); + private void _loadUrl(int id, String url) { + InternalWebView.loadUrl(id, url); + // InternalWebView target = webViews.get(id); + // target.loadUrl(url); + + } + + // private native void _dispose(int id); + private void _dispose(int id) { + InternalWebView.dispose(id); + } + + // private native void _loadContent(int id, String content, String contentType); + private void _loadContent(int id, String content, String contentType) { + InternalWebView.loadContent(id, content, contentType); + } private native void _setEncoding(int id, String encoding); } diff -r e8beeeaa062e modules/web/src/android/java/com/sun/webkit/WebPage.java --- a/modules/web/src/android/java/com/sun/webkit/WebPage.java Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/android/java/com/sun/webkit/WebPage.java Fri May 08 22:20:14 2015 +0200 @@ -25,22 +25,27 @@ package com.sun.webkit; +import java.io.StringReader; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.LinkedList; import java.util.List; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import javafx.scene.Scene; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Window; import org.w3c.dom.Document; +import org.xml.sax.InputSource; public final class WebPage { static { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { +System.out.println ("[JVDBG] I will load the webview system library"); System.loadLibrary("webview"); return null; } @@ -97,7 +102,8 @@ } public void dropRenderFrames() { - System.out.println("--> dropRenderFrames"); + // this should not be called if webview is not shown + // System.out.println("--> dropRenderFrames"); } public boolean isDragConfirmed() { @@ -152,7 +158,17 @@ } public Document getDocument(long mainFrame) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + String xmlString = getNativePeer().getHtmlContent(); + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.parse(new InputSource(new StringReader(xmlString))); + return document; + } + catch (Exception e) { + System.err.println ("Cannot parse "+xmlString+" due to "+e); + return null; + } } public void setDeveloperExtrasEnabled(boolean b) { diff -r e8beeeaa062e modules/web/src/android/native/android_webview.c --- a/modules/web/src/android/native/android_webview.c Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/android/native/android_webview.c Fri May 08 22:20:14 2015 +0200 @@ -50,6 +50,8 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; +LOGV(TAG, "ONLOAD WEB"); +LOGI("ONLOAD WEB"); if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6)) { return JNI_ERR; /* JNI version not supported */ } @@ -60,8 +62,10 @@ } void init_ids(JNIEnv *env) { +LOGI("INIT IDS"); jInternalWebViewClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/oracle/dalvik/InternalWebView")); +LOGI("Got webviewclass: %p",jInternalWebViewClass); CHECK_EXCEPTION(env); jInternalWebView_init = (*env)->GetMethodID(env, jInternalWebViewClass, "", "()V"); @@ -140,19 +144,27 @@ free(fullpath); } -int create_android_webview() { - JNIEnv *env; +/* +int create_android_webview(JNIEnv *env) { + LOGI("CREATE_ANDROID_WEBVIEW1A"); + //JNIEnv *env; + LOGI("CREATE_ANDROID_WEBVIEW1B"); int internalID = -1; - (*jvm)->AttachCurrentThread(jvm, &env, 0); + LOGI("CREATE_ANDROID_WEBVIEW1C"); + //(*jvm)->AttachCurrentThread(jvm, &env, 0); + LOGI("CREATE_ANDROID_WEBVIEW2a\n"); jobject jInternalWebView = (*env)->NewGlobalRef(env, (*env)->NewObject(env, jInternalWebViewClass, jInternalWebView_init)); + LOGI(TAG,"CREATE_ANDROID_WEBVIEW3\n"); CHECK_EXCEPTION(env); internalID = (*env)->CallIntMethod(env, jInternalWebView, jInternalWebView_getInternalID); + LOGI(TAG, "CREATE_ANDROID_WEBVIEW4\n"); CHECK_EXCEPTION(env); return internalID; } +*/ void move_and_resize(int id, int x, int y, int w, int h) { JNIEnv *env; @@ -161,6 +173,7 @@ jInternalWebView_moveAndResize, id, x, y, w, h); } +/* void set_visible(int id, int visible) { JNIEnv *env; (*jvm)->AttachCurrentThread(jvm, &env, 0); @@ -168,12 +181,13 @@ jInternalWebView_setVisible, id, visible == 1 ? JNI_TRUE : JNI_FALSE); } - +*/ void move_to_top(int id) { JNIEnv *env; (*jvm)->AttachCurrentThread(jvm, &env, 0); } +/* void load_url(int id, const char *curl) { JNIEnv *env; if (!curl) { @@ -184,6 +198,7 @@ (*env)->CallStaticVoidMethod(env, jInternalWebViewClass, jInternalWebView_loadUrl, id, jurl); CHECK_EXCEPTION(env); } +*/ void load_content(int id, const char *content, const char *content_type) { JNIEnv *env; @@ -221,7 +236,7 @@ /* * Class: com_oracle_dalvik_InternalWebView * Method: _fireLoadEvent - * Signature: (IIILjava/lang/String;Ljava/lang/String;DI)V + * Signature: (IIILjava/lang/String;Ljava/lang/String;II)V */ JNIEXPORT void JNICALL Java_com_oracle_dalvik_InternalWebView__1fireLoadEvent (JNIEnv *env, jobject view, jint id, jint frameID, jint state, jstring url, diff -r e8beeeaa062e modules/web/src/android/native/android_webview.h --- a/modules/web/src/android/native/android_webview.h Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/android/native/android_webview.h Fri May 08 22:20:14 2015 +0200 @@ -34,7 +34,7 @@ void init_functions(JNIEnv *); - int create_android_webview(); + int create_android_webview(JNIEnv *); void move_and_resize(int id, jint x, int y, int w, int h); diff -r e8beeeaa062e modules/web/src/android/native/native_webview.c --- a/modules/web/src/android/native/native_webview.c Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/android/native/native_webview.c Fri May 08 22:20:14 2015 +0200 @@ -23,12 +23,13 @@ * questions. */ +// WARNING: WE ARE REPLACING THESE CALLS WITH JAVA CALLS #include "com_sun_webkit_NativeWebView.h" #include "symbol.h" #define LIBANDROID_WEBVIEW_SO "libandroid_webview.so" -static jint(*_ANDROID_create_android_webview)(); +static jint(*_ANDROID_create_android_webview)(JNIEnv *); static void (*_ANDROID_move_and_resize)(int id, int x, int y, int w, int h); static void (*_ANDROID_set_visible)(int id, int visible); static void (*_ANDROID_move_to_top)(int id); @@ -109,7 +110,7 @@ */ JNIEXPORT jint JNICALL Java_com_sun_webkit_NativeWebView__1createAndroidWebView (JNIEnv *env, jobject view) { - return (*_ANDROID_create_android_webview)(); + return (*_ANDROID_create_android_webview)(env); } /* diff -r e8beeeaa062e modules/web/src/ios/java/com/sun/javafx/scene/web/behavior/HTMLEditorBehavior.java --- a/modules/web/src/ios/java/com/sun/javafx/scene/web/behavior/HTMLEditorBehavior.java Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/ios/java/com/sun/javafx/scene/web/behavior/HTMLEditorBehavior.java Fri May 08 22:20:14 2015 +0200 @@ -65,7 +65,7 @@ HTMLEditorSkin editorSkin = (HTMLEditorSkin)editor.getSkin(); editorSkin.keyboardShortcuts(name); } else if ("F12".equals(name)) { - getControl().getImpl_traversalEngine().getTopLeftFocusableNode(); + getControl().getImpl_traversalEngine().selectFirst().requestFocus(); } else { super.callAction(name); } diff -r e8beeeaa062e modules/web/src/ios/java/com/sun/javafx/scene/web/skin/HTMLEditorSkin.java --- a/modules/web/src/ios/java/com/sun/javafx/scene/web/skin/HTMLEditorSkin.java Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/ios/java/com/sun/javafx/scene/web/skin/HTMLEditorSkin.java Fri May 08 22:20:14 2015 +0200 @@ -28,6 +28,13 @@ import java.util.ResourceBundle; import com.sun.javafx.application.PlatformImpl; +import com.sun.javafx.scene.traversal.Algorithm; +import com.sun.javafx.scene.traversal.Direction; +import com.sun.javafx.scene.traversal.ParentTraversalEngine; +import com.sun.javafx.scene.traversal.TraversalContext; +import javafx.geometry.Orientation; +import org.w3c.dom.html.HTMLDocument; +import org.w3c.dom.html.HTMLElement; import javafx.application.ConditionalFeature; import javafx.application.Platform; @@ -232,7 +239,7 @@ return DEFAULT_WINDOWS_7_MAPPINGS; } - private TraversalEngine engine; + private ParentTraversalEngine engine; private boolean resetToolbarState = false; private String cachedHTMLText = ""; @@ -474,9 +481,22 @@ enableToolbar(true); setHTMLText(cachedHTMLText); - engine = new TraversalEngine(getSkinnable(), false); - engine.addTraverseListener(this); - engine.reg(toolbar1); + engine = new ParentTraversalEngine(getSkinnable(), new Algorithm() { + @Override + public Node select(Node owner, Direction dir, TraversalContext context) { + return cutButton; + } + + @Override + public Node selectFirst(TraversalContext context) { + return cutButton; + } + + @Override + public Node selectLast(TraversalContext context) { + return cutButton; + } + }); getSkinnable().setImpl_traversalEngine(engine); webView.setFocusTraversable(true); gridPane.getChildren().addListener(itemsListener); diff -r e8beeeaa062e modules/web/src/ios/java/javafx/scene/web/JS2JavaBridge.java --- a/modules/web/src/ios/java/javafx/scene/web/JS2JavaBridge.java Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/ios/java/javafx/scene/web/JS2JavaBridge.java Fri May 08 22:20:14 2015 +0200 @@ -122,8 +122,13 @@ getJavaBridge()).append("['").append(jsName).append("'])"); Integer jsId = (Integer) webEngine.executeScript(sb.toString()); - exportedObjectsByJSIds.put(jsId.toString(), jsObj); - jsIdsByExportedObjects.put(jsObj, jsId.toString()); + if (jsId != null) { + exportedObjectsByJSIds.put(jsId.toString(), jsObj); + jsIdsByExportedObjects.put(jsObj, jsId.toString()); + } + else { + System.out.println("[JVDBG] Error, jsId = null for "+jsName); + } log("populateObject<>> onVisibilityChangedProperty() { return onVisibilityChanged; } - private ObjectProperty createPopupHandler - = new SimpleObjectProperty(this, "createPopupHandler", - new Callback() { - public WebEngine call(Object o) { - return WebEngine.this; - } - }); + private final ObjectProperty> createPopupHandler + = new SimpleObjectProperty>(this, "createPopupHandler", + p -> WebEngine.this); /** * Returns the JavaScript popup handler. * @see #createPopupHandlerProperty * @see #setCreatePopupHandler */ - public final Callback getCreatePopupHandler() { return createPopupHandler.get(); } + public final Callback getCreatePopupHandler() { return createPopupHandler.get(); } /** * Sets the JavaScript popup handler. @@ -413,7 +413,7 @@ * @see #getCreatePopupHandler * @see PopupFeatures */ - public final void setCreatePopupHandler(Callback handler) { createPopupHandler.set(handler); } + public final void setCreatePopupHandler(Callback handler) { createPopupHandler.set(handler); } /** * JavaScript popup handler property. This handler is invoked when a script @@ -427,10 +427,10 @@ * * @see PopupFeatures */ - public final ObjectProperty createPopupHandlerProperty() { return createPopupHandler; } + public final ObjectProperty> createPopupHandlerProperty() { return createPopupHandler; } - private ObjectProperty> confirmHandler + private final ObjectProperty> confirmHandler = new SimpleObjectProperty>(this, "confirmHandler"); /** @@ -456,8 +456,8 @@ public final ObjectProperty> confirmHandlerProperty() { return confirmHandler; } - private ObjectProperty promptHandler - = new SimpleObjectProperty(this, "promptHandler"); + private final ObjectProperty> promptHandler + = new SimpleObjectProperty>(this, "promptHandler"); /** * Returns the JavaScript {@code prompt} handler. @@ -465,7 +465,7 @@ * @see #setPromptHandler * @see PromptData */ - public final Callback getPromptHandler() { return promptHandler.get(); } + public final Callback getPromptHandler() { return promptHandler.get(); } /** * Sets the JavaScript {@code prompt} handler. @@ -473,7 +473,7 @@ * @see #getPromptHandler * @see PromptData */ - public final void setPromptHandler(Callback handler) { promptHandler.set(handler); } + public final void setPromptHandler(Callback handler) { promptHandler.set(handler); } /** * JavaScript {@code prompt} handler property. This handler is invoked @@ -483,7 +483,29 @@ * * @see PromptData */ - public final ObjectProperty promptHandlerProperty() { return promptHandler; } + public final ObjectProperty> promptHandlerProperty() { return promptHandler; } + + /** + * The event handler called when an error occurs. + * + * @defaultValue {@code null} + * @since JavaFX 8.0 + */ + private final ObjectProperty> onError = + new SimpleObjectProperty<>(this, "onError"); + + public final EventHandler getOnError() { + return onError.get(); + } + + public final void setOnError(EventHandler handler) { + onError.set(handler); + } + + public final ObjectProperty> onErrorProperty() { + return onError; + } + /** * Creates a new engine. @@ -627,31 +649,29 @@ /** * Drives the {@code Timer} when {@code Timer.Mode.PLATFORM_TICKS} is set. */ - private static class PulseTimer { + private static final class PulseTimer { // Used just to guarantee constant pulse activity. See RT-14433. - private static AnimationTimer animation = + private static final AnimationTimer animation = new AnimationTimer() { @Override public void handle(long l) {} }; - private static TKPulseListener listener = - new TKPulseListener() { - public void pulse() { + private static final TKPulseListener listener = + () -> { // Note, the timer event is executed right in the notifyTick(), // that is during the pulse event. This makes the timer more // repsonsive, though prolongs the pulse. So far it causes no // problems but nevertheless it should be kept in mind. //Timer.getTimer().notifyTick(); - } - }; + }; - public static void start(){ + private static void start(){ Toolkit.getToolkit().addSceneTkPulseListener(listener); animation.start(); } - public static void stop() { + private static void stop() { Toolkit.getToolkit().removeSceneTkPulseListener(listener); animation.stop(); } @@ -661,12 +681,12 @@ Toolkit.getToolkit().checkFxUserThread(); } - private class LoadWorker implements Worker { + private final class LoadWorker implements Worker { private final ReadOnlyObjectWrapper state = new ReadOnlyObjectWrapper(this, "state", State.READY); @Override public final State getState() { checkThread(); return state.get(); } @Override public final ReadOnlyObjectProperty stateProperty() { checkThread(); return state.getReadOnlyProperty(); } - private final void updateState(State value) { + private void updateState(State value) { checkThread(); this.state.set(value); running.set(value == State.SCHEDULED || value == State.RUNNING); @@ -758,7 +778,8 @@ } private void dispatchLoadEvent(long frame, int state, - String url, String contentType, double workDone, int errorCode) { + String url, String contentType, double workDone, int errorCode) + { } Throwable describeError(int errorCode) { @@ -767,7 +788,8 @@ return new Throwable(reason); } } - + + private final class DocumentProperty extends ReadOnlyObjectPropertyBase { @@ -786,11 +808,12 @@ if (!this.available) { return null; } - if (this.document == null) { - if (this.document == null) { - this.available = false; - } - } + this.document = getCurrentDocument(); + // if (this.document == null) { + // if (this.document == null) { + // this.available = false; + // } + // } return this.document; } @@ -862,11 +885,29 @@ } } - void notifyLoadFinished() { + private String pageContent; + + Document getCurrentDocument () { + Document document = null; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + document = builder.parse(new InputSource(new StringReader(pageContent))); + } + catch (Exception e) { + e.printStackTrace(); + } + return document; + } + + void notifyLoadFinished(String loc, String content) { synchronized (loadedLock) { + this.pageContent = ""+content+""; loaded = true; updateProgress(1.0); updateState(Worker.State.SUCCEEDED); + location.set(loc); + document.invalidate(true); if (pageListener != null) { pageListener.onLoadFinished(); } diff -r e8beeeaa062e modules/web/src/ios/java/javafx/scene/web/WebErrorEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/web/src/ios/java/javafx/scene/web/WebErrorEvent.java Fri May 08 22:20:14 2015 +0200 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javafx.scene.web; + +import javafx.beans.NamedArg; +import javafx.event.Event; +import javafx.event.EventType; + +/** + * An event indicating a {@link javafx.scene.web.WebEngine} error. + * Holds an optional text message and an optional exception + * associated with the error. + * + * @see javafx.scene.web.WebEngine#onErrorProperty WebEngine.onError + * @since JavaFX 8.0 + */ +public final class WebErrorEvent extends Event { + + /** + * Common supertype for all {@code WebErrorEvent} types. + */ + public static final EventType ANY = + new EventType(Event.ANY, "WEB_ERROR"); + + /** + * This event occurs when a {@link javafx.scene.web.WebEngine} detects that its + * user data directory is already in use by a {@code WebEngine} + * running in a different VM. + * + *

In general, multiple {@code WebEngine} instances may share + * a single user data directory as long as they run in the same + * VM. {@code WebEngine} instances running in different VMs are + * not allowed to share the same user data directory. + * + *

When a {@code WebEngine} is about to start loading a web + * page or executing a script for the first time, it checks whether + * its {@link javafx.scene.web.WebEngine#userDataDirectoryProperty userDataDirectory} + * is already in use by a {@code WebEngine} running in a different + * VM. If the latter is the case, the {@code WebEngine} invokes the + * {@link javafx.scene.web.WebEngine#onErrorProperty WebEngine.onError} event handler, + * if any, with a {@code USER_DATA_DIRECTORY_ALREADY_IN_USE} event. + * If the invoked event handler modifies the {@code userDataDirectory} + * property, the {@code WebEngine} retries with the new user + * data directory as soon as the handler returns. If the handler + * does not modify the {@code userDataDirectory} property (which + * is the default), the {@code WebEngine} continues without the + * user data directory. + */ + public static final EventType + USER_DATA_DIRECTORY_ALREADY_IN_USE = new EventType( + WebErrorEvent.ANY, "USER_DATA_DIRECTORY_ALREADY_IN_USE"); + + /** + * This event occurs when a {@link javafx.scene.web.WebEngine} encounters + * an I/O error while trying to create or access the user + * data directory. + * + *

When a {@code WebEngine} is about to start loading a web + * page or executing a script for the first time, it checks whether + * it can create or access its + * {@link javafx.scene.web.WebEngine#userDataDirectoryProperty userDataDirectory}. + * If the check fails with an I/O error (such as {@code + * java.io.IOException}), the {@code WebEngine} invokes the {@link + * javafx.scene.web.WebEngine#onErrorProperty WebEngine.onError} event handler, + * if any, with a {@code USER_DATA_DIRECTORY_IO_ERROR} event. + * If the invoked event handler modifies the {@code userDataDirectory} + * property, the {@code WebEngine} retries with the new user + * data directory as soon as the handler returns. If the handler + * does not modify the {@code userDataDirectory} property (which + * is the default), the {@code WebEngine} continues without the + * user data directory. + */ + public static final EventType + USER_DATA_DIRECTORY_IO_ERROR = new EventType( + WebErrorEvent.ANY, "USER_DATA_DIRECTORY_IO_ERROR"); + + /** + * This event occurs when a {@link javafx.scene.web.WebEngine} encounters + * a security error while trying to create or access the user + * data directory. + * + *

When a {@code WebEngine} is about to start loading a web + * page or executing a script for the first time, it checks whether + * it can create or access its + * {@link javafx.scene.web.WebEngine#userDataDirectoryProperty userDataDirectory}. + * If the check fails with a security error (such as {@code + * java.lang.SecurityException}), the {@code WebEngine} invokes the + * {@link javafx.scene.web.WebEngine#onErrorProperty WebEngine.onError} event handler, + * if any, with a {@code USER_DATA_DIRECTORY_SECURITY_ERROR} event. + * If the invoked event handler modifies the {@code userDataDirectory} + * property, the {@code WebEngine} retries with the new user + * data directory as soon as the handler returns. If the handler + * does not modify the {@code userDataDirectory} property (which + * is the default), the {@code WebEngine} continues without the + * user data directory. + */ + public static final EventType + USER_DATA_DIRECTORY_SECURITY_ERROR = new EventType( + WebErrorEvent.ANY, "USER_DATA_DIRECTORY_SECURITY_ERROR"); + + + private final String message; + private final Throwable exception; + + + /** + * Creates a new {@code WebErrorEvent}. + * @param source the event source which sent the event + * @param eventType the event type + * @param message the text message associated with the event; + * may be {@code null} + * @param exception the exception associated with the event; + * may be {@code null} + */ + public WebErrorEvent(@NamedArg("source") Object source, @NamedArg("type") EventType type, + @NamedArg("message") String message, @NamedArg("exception") Throwable exception) + { + super(source, null, type); + this.message = message; + this.exception = exception; + } + + + /** + * Returns the text message associated with this event. + * @return the text message associated with this event, or + * {@code null} if there is no such message + */ + public String getMessage() { + return message; + } + + /** + * Returns the exception associated with this event. + * @return the exception associated with this event, or + * {@code null} if there is no such exception + */ + public Throwable getException() { + return exception; + } + + /** + * {@inheritDoc} + */ + @Override public String toString() { + return String.format("WebErrorEvent [source = %s, eventType = %s, " + + "message = \"%s\", exception = %s]", + getSource(), getEventType(), getMessage(), getException()); + } +} diff -r e8beeeaa062e modules/web/src/ios/java/javafx/scene/web/WebEvent.java --- a/modules/web/src/ios/java/javafx/scene/web/WebEvent.java Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/ios/java/javafx/scene/web/WebEvent.java Fri May 08 22:20:14 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package javafx.scene.web; +import javafx.beans.NamedArg; import javafx.event.Event; import javafx.event.EventType; @@ -79,7 +81,7 @@ /** * Creates a new event object. */ - public WebEvent(Object source, EventType type, T data) { + public WebEvent(@NamedArg("source") Object source, @NamedArg("type") EventType type, @NamedArg("data") T data) { super(source, null, type); this.data = data; } diff -r e8beeeaa062e modules/web/src/ios/java/javafx/scene/web/WebView.java --- a/modules/web/src/ios/java/javafx/scene/web/WebView.java Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/ios/java/javafx/scene/web/WebView.java Fri May 08 22:20:14 2015 +0200 @@ -63,7 +63,7 @@ * afterwards. {@code WebView} handles mouse and some keyboard events, and * manages scrolling automatically, so there's no need to put it into a * {@code ScrollPane}. - * + * *

{@code WebView} objects must be created and accessed solely from the * FX thread. * @since JavaFX 2.0 @@ -200,27 +200,17 @@ */ private DoubleProperty fontScale; - /** - * Sets scale factor applied to font. - */ public final void setFontScale(double value) { WebEngine.checkThread(); fontScaleProperty().set(value); } - /** - * Returns scale factor applied to font. - */ public final double getFontScale() { return (this.fontScale != null) ? this.fontScale.get() : DEFAULT_FONT_SCALE; } - /** - * Scale factor applied to font. This property can be used to make text - * larger or smaller but does not affect images and fixed size elements. - */ public DoubleProperty fontScaleProperty() { if (fontScale == null) { fontScale = new StyleableDoubleProperty(DEFAULT_FONT_SCALE) { @@ -252,17 +242,10 @@ engine = new WebEngine(); engine.setView(this); - stagePulseListener = new TKPulseListener() { - @Override public void pulse() { - handleStagePulse(); - } + stagePulseListener = () -> { + handleStagePulse(); }; - focusedProperty().addListener(new ChangeListener() { - - @Override - public void changed(ObservableValue ov, Boolean t, Boolean t1) { - - } + focusedProperty().addListener((ov, t, t1) -> { }); Toolkit.getToolkit().addStageTkPulseListener(stagePulseListener); @@ -422,7 +405,7 @@ return minWidth; } private DoubleProperty minWidth; - + /** * Sets minimum width. */ @@ -430,7 +413,7 @@ minWidthProperty().set(value); _setWidth(handle, value); } - + /** * Returns minimum width. */ @@ -469,7 +452,7 @@ return minHeight; } private DoubleProperty minHeight; - + /** * Sets minimum height. */ @@ -477,7 +460,7 @@ minHeightProperty().set(value); _setHeight(handle, value); } - + /** * Sets minimum height. */ @@ -526,7 +509,7 @@ return prefWidth; } private DoubleProperty prefWidth; - + /** * Sets preferred width. */ @@ -573,7 +556,7 @@ return prefHeight; } private DoubleProperty prefHeight; - + /** * Sets preferred height. */ @@ -581,7 +564,7 @@ prefHeightProperty().set(value); _setHeight(handle, value); } - + /** * Returns preferred height. */ @@ -630,7 +613,7 @@ return maxWidth; } private DoubleProperty maxWidth; - + /** * Sets maximum width. */ @@ -638,7 +621,7 @@ maxWidthProperty().set(value); _setWidth(handle, value); } - + /** * Returns maximum width. */ @@ -677,7 +660,7 @@ return maxHeight; } private DoubleProperty maxHeight; - + /** * Sets maximum height. */ @@ -685,7 +668,7 @@ maxHeightProperty().set(value); _setHeight(handle, value); } - + /** * Returns maximum height. */ @@ -704,7 +687,8 @@ _setWidth(handle, maxWidth); _setHeight(handle, maxHeight); } - + + /** * Specifies a requested font smoothing type : gray or LCD. * @@ -721,7 +705,7 @@ public final void setFontSmoothingType(FontSmoothingType value) { fontSmoothingTypeProperty().set(value); } - + public final FontSmoothingType getFontSmoothingType() { return (this.fontSmoothingType != null) ? this.fontSmoothingType.get() @@ -792,7 +776,7 @@ } return contextMenuEnabled; } - + /** * Super-lazy instantiation pattern from Bill Pugh. */ @@ -981,7 +965,7 @@ public List> getCssMetaData() { return getClassCssMetaData(); } - + // event handling private void handleStagePulse() { @@ -1003,13 +987,15 @@ && getScene() != null && getScene().getWindow() != null && getScene().getWindow().isShowing(); - + if (reallyVisible) { if (impl_isDirty(DirtyBits.WEBVIEW_VIEW)) { Scene.impl_setAllowPGAccess(true); //getPGWebView().update(); // creates new render queues Scene.impl_setAllowPGAccess(false); } + } else { + _setVisible(handle, false); } } @@ -1025,7 +1011,7 @@ @Override protected ObservableList getChildren() { return super.getChildren(); } - + // Node stuff /** @@ -1096,8 +1082,8 @@ private void notifyLoadStarted() { engine.notifyLoadStarted(); } - private void notifyLoadFinished() { - engine.notifyLoadFinished(); + private void notifyLoadFinished(String loc, String content) { + engine.notifyLoadFinished(loc, content); } private void notifyLoadFailed() { engine.notifyLoadFailed(); diff -r e8beeeaa062e modules/web/src/ios/native/WebViewImpl.h --- a/modules/web/src/ios/native/WebViewImpl.h Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/ios/native/WebViewImpl.h Fri May 08 22:20:14 2015 +0200 @@ -29,8 +29,13 @@ #include +#ifdef __LP64__ +#define jlong_to_ptr(a) ((void*)(a)) +#define ptr_to_jlong(a) ((jlong)(a)) +#else #define jlong_to_ptr(a) ((void*)(int)(a)) #define ptr_to_jlong(a) ((jlong)(int)(a)) +#endif @interface WebViewImpl : NSObject { UIWebView *webView; diff -r e8beeeaa062e modules/web/src/ios/native/WebViewImpl.m --- a/modules/web/src/ios/native/WebViewImpl.m Fri May 08 12:39:39 2015 +0300 +++ b/modules/web/src/ios/native/WebViewImpl.m Fri May 08 22:20:14 2015 +0200 @@ -110,7 +110,8 @@ jObject = (*env)->NewGlobalRef(env, object); jclass cls = (*env)->GetObjectClass(env, object); jmidLoadStarted = (*env)->GetMethodID(env, cls, "notifyLoadStarted", "()V"); - jmidLoadFinished = (*env)->GetMethodID(env, cls, "notifyLoadFinished", "()V"); + // jmidLoadFinished = (*env)->GetMethodID(env, cls, "notifyLoadFinished", "()V"); + jmidLoadFinished = (*env)->GetMethodID(env, cls, "notifyLoadFinished", "(Ljava/lang/String;Ljava/lang/String;)V"); jmidLoadFailed = (*env)->GetMethodID(env, cls, "notifyLoadFailed", "()V"); jmidJavaCall = (*env)->GetMethodID(env, cls, "notifyJavaCall", "(Ljava/lang/String;)V"); if (jmidLoadStarted == 0 || jmidLoadFinished == 0 || jmidLoadFailed == 0 || jmidJavaCall == 0) { @@ -187,7 +188,6 @@ - (BOOL)webView:(UIWebView *)wv shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSString *url = [[request URL] absoluteString]; - //NSLog(@"WebViewImpl request load: %@", url); if ([url hasPrefix:JAVA_CALL_PREFIX]) { JNIEnv *env = [self getJNIEnv]; if (env != NULL) { @@ -214,6 +214,10 @@ } - (void)webViewDidFinishLoad:(UIWebView *)wv{ + NSString *inner = [wv stringByEvaluatingJavaScriptFromString: + @"document.documentElement.innerHTML"]; + NSString *currentUrl = wv.request.URL.absoluteString; + loadingLabel.hidden = YES; [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; if (windowView) { @@ -224,12 +228,16 @@ JNIEnv *env = [self getJNIEnv]; if (env != NULL) { - (*env)->CallVoidMethod(env, jObject, jmidLoadFinished); + jstring jInner = createJString(env, inner); + jstring jUrl = createJString(env, currentUrl); + (*env)->CallVoidMethod(env, jObject, jmidLoadFinished, jUrl, jInner); [self releaseJNIEnv:env]; } } - (void)webView:(UIWebView *)wv didFailLoadWithError:(NSError *)error { + NSLog(@"WebViewImpl ERROR: didFailLoadWithError"); + NSLog(@" this error => %@ ", [error userInfo] ); JNIEnv *env = [self getJNIEnv]; if (env != NULL) { (*env)->CallVoidMethod(env, jObject, jmidLoadFailed);