Decouple JsResult from the WebViewClassic impl

The majority  JsResult and JsPromptResult are simple data-carrying &
callback classes. Extract the WebViewClassic specific parts dealing
with WebCore thread and CallbackProxy and decouple via abstract interface.

Bug: 6238755
Change-Id: Ibafd18910725f0875e3b59c1b2078173c102cdec
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 800ebc8..cb74ed6 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -147,6 +147,40 @@
         }
     }
 
+    private class JsResultReceiver implements JsResult.ResultReceiver {
+        // This prevents a user from interacting with the result before WebCore is
+        // ready to handle it.
+        private boolean mReady;
+        // Tells us if the user tried to confirm or cancel the result before WebCore
+        // is ready.
+        private boolean mTriedToNotifyBeforeReady;
+
+        public JsPromptResult mJsResult = new JsPromptResult(this);
+
+        final void setReady() {
+            mReady = true;
+            if (mTriedToNotifyBeforeReady) {
+                notifyCallbackProxy();
+            }
+        }
+
+        /* Wake up the WebCore thread. */
+        @Override
+        public void onJsResultComplete(JsResult result) {
+            if (mReady) {
+                notifyCallbackProxy();
+            } else {
+                mTriedToNotifyBeforeReady = true;
+            }
+        }
+
+        private void notifyCallbackProxy() {
+            synchronized (CallbackProxy.this) {
+                CallbackProxy.this.notify();
+            }
+        }
+}
+
     /**
      * Construct a new CallbackProxy.
      */
@@ -531,14 +565,15 @@
 
             case JS_ALERT:
                 if (mWebChromeClient != null) {
-                    final JsResult res = (JsResult) msg.obj;
+                    final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+                    final JsResult res = receiver.mJsResult;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
                     if (!mWebChromeClient.onJsAlert(mWebView.getWebView(), url, message,
                             res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
-                            res.setReady();
+                            receiver.setReady();
                             break;
                         }
                         new AlertDialog.Builder(mContext)
@@ -561,20 +596,21 @@
                                         })
                                 .show();
                     }
-                    res.setReady();
+                    receiver.setReady();
                 }
                 break;
 
             case JS_CONFIRM:
                 if (mWebChromeClient != null) {
-                    final JsResult res = (JsResult) msg.obj;
+                    final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+                    final JsResult res = receiver.mJsResult;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
                     if (!mWebChromeClient.onJsConfirm(mWebView.getWebView(), url, message,
                             res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
-                            res.setReady();
+                            receiver.setReady();
                             break;
                         }
                         new AlertDialog.Builder(mContext)
@@ -605,13 +641,14 @@
                     }
                     // Tell the JsResult that it is ready for client
                     // interaction.
-                    res.setReady();
+                    receiver.setReady();
                 }
                 break;
 
             case JS_PROMPT:
                 if (mWebChromeClient != null) {
-                    final JsPromptResult res = (JsPromptResult) msg.obj;
+                    final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+                    final JsPromptResult res = receiver.mJsResult;
                     String message = msg.getData().getString("message");
                     String defaultVal = msg.getData().getString("default");
                     String url = msg.getData().getString("url");
@@ -619,7 +656,7 @@
                                 defaultVal, res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
-                            res.setReady();
+                            receiver.setReady();
                             break;
                         }
                         final LayoutInflater factory = LayoutInflater
@@ -662,20 +699,21 @@
                     }
                     // Tell the JsResult that it is ready for client
                     // interaction.
-                    res.setReady();
+                    receiver.setReady();
                 }
                 break;
 
             case JS_UNLOAD:
                 if (mWebChromeClient != null) {
-                    final JsResult res = (JsResult) msg.obj;
+                    final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+                    final JsResult res = receiver.mJsResult;
                     String message = msg.getData().getString("message");
                     String url = msg.getData().getString("url");
                     if (!mWebChromeClient.onJsBeforeUnload(mWebView.getWebView(), url,
                             message, res)) {
                         if (!canShowAlertDialog()) {
                             res.cancel();
-                            res.setReady();
+                            receiver.setReady();
                             break;
                         }
                         final String m = mContext.getString(
@@ -700,19 +738,20 @@
                                         })
                                 .show();
                     }
-                    res.setReady();
+                    receiver.setReady();
                 }
                 break;
 
             case JS_TIMEOUT:
                 if(mWebChromeClient != null) {
-                    final JsResult res = (JsResult) msg.obj;
+                    final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
+                    final JsResult res = receiver.mJsResult;
                     if(mWebChromeClient.onJsTimeout()) {
                         res.confirm();
                     } else {
                         res.cancel();
                     }
-                    res.setReady();
+                    receiver.setReady();
                 }
                 break;
 
@@ -1331,7 +1370,7 @@
         if (mWebChromeClient == null) {
             return;
         }
-        JsResult result = new JsResult(this, false);
+        JsResultReceiver result = new JsResultReceiver();
         Message alert = obtainMessage(JS_ALERT, result);
         alert.getData().putString("message", message);
         alert.getData().putString("url", url);
@@ -1352,7 +1391,7 @@
         if (mWebChromeClient == null) {
             return false;
         }
-        JsResult result = new JsResult(this, false);
+        JsResultReceiver result = new JsResultReceiver();
         Message confirm = obtainMessage(JS_CONFIRM, result);
         confirm.getData().putString("message", message);
         confirm.getData().putString("url", url);
@@ -1365,7 +1404,7 @@
                 Log.e(LOGTAG, Log.getStackTraceString(e));
             }
         }
-        return result.getResult();
+        return result.mJsResult.getResult();
     }
 
     public String onJsPrompt(String url, String message, String defaultValue) {
@@ -1374,7 +1413,7 @@
         if (mWebChromeClient == null) {
             return null;
         }
-        JsPromptResult result = new JsPromptResult(this);
+        JsResultReceiver result = new JsResultReceiver();
         Message prompt = obtainMessage(JS_PROMPT, result);
         prompt.getData().putString("message", message);
         prompt.getData().putString("default", defaultValue);
@@ -1388,7 +1427,7 @@
                 Log.e(LOGTAG, Log.getStackTraceString(e));
             }
         }
-        return result.getStringResult();
+        return result.mJsResult.getStringResult();
     }
 
     public boolean onJsBeforeUnload(String url, String message) {
@@ -1397,7 +1436,7 @@
         if (mWebChromeClient == null) {
             return true;
         }
-        JsResult result = new JsResult(this, true);
+        JsResultReceiver result = new JsResultReceiver();
         Message confirm = obtainMessage(JS_UNLOAD, result);
         confirm.getData().putString("message", message);
         confirm.getData().putString("url", url);
@@ -1410,7 +1449,7 @@
                 Log.e(LOGTAG, Log.getStackTraceString(e));
             }
         }
-        return result.getResult();
+        return result.mJsResult.getResult();
     }
 
     /**
@@ -1540,7 +1579,7 @@
         if (mWebChromeClient == null) {
             return true;
         }
-        JsResult result = new JsResult(this, true);
+        JsResultReceiver result = new JsResultReceiver();
         Message timeout = obtainMessage(JS_TIMEOUT, result);
         synchronized (this) {
             sendMessage(timeout);
@@ -1551,7 +1590,7 @@
                 Log.e(LOGTAG, Log.getStackTraceString(e));
             }
         }
-        return result.getResult();
+        return result.mJsResult.getResult();
     }
 
     public void getVisitedHistory(ValueCallback<String[]> callback) {
diff --git a/core/java/android/webkit/JsPromptResult.java b/core/java/android/webkit/JsPromptResult.java
index 9fcd1bc..a1bf124 100644
--- a/core/java/android/webkit/JsPromptResult.java
+++ b/core/java/android/webkit/JsPromptResult.java
@@ -18,11 +18,11 @@
 
 
 /**
- * Public class for handling javascript prompt requests. A
- * JsDialogHandlerInterface implentation will receive a jsPrompt call with a
- * JsPromptResult parameter. This parameter is used to return a result to
- * WebView. The client can call cancel() to cancel the dialog or confirm() with
- * the user's input to confirm the dialog.
+ * Public class for handling JavaScript prompt requests. The WebChromeClient will receive a
+ * {@link WebChromeClient#onJsPrompt(WebView, String, String, String, JsPromptResult)} call with a
+ * JsPromptResult instance as a parameter. This parameter is used to return the result of this user
+ * dialog prompt back to the WebView instance. The client can call cancel() to cancel the dialog or
+ * confirm() with the user's input to confirm the dialog.
  */
 public class JsPromptResult extends JsResult {
     // String result of the prompt
@@ -36,17 +36,17 @@
         confirm();
     }
 
-    /*package*/ JsPromptResult(CallbackProxy proxy) {
-        super(proxy, /* unused */ false);
+    /**
+     * @hide Only for use by WebViewProvider implementations
+     */
+    public JsPromptResult(ResultReceiver receiver) {
+        super(receiver);
     }
 
-    /*package*/ String getStringResult() {
+    /**
+     * @hide Only for use by WebViewProvider implementations
+     */
+    public String getStringResult() {
         return mStringResult;
     }
-
-    @Override
-    /*package*/ void handleDefault() {
-        mStringResult = null;
-        super.handleDefault();
-    }
 }
diff --git a/core/java/android/webkit/JsResult.java b/core/java/android/webkit/JsResult.java
index e61ab21..07b686f 100644
--- a/core/java/android/webkit/JsResult.java
+++ b/core/java/android/webkit/JsResult.java
@@ -16,23 +16,27 @@
 
 package android.webkit;
 
-
+/**
+ * An instance of this class is passed as a parameter in various {@link WebChromeClient} action
+ * notifications. The object is used as a handle onto the underlying JavaScript-originated request,
+ * and provides a means for the client to indicate whether this action should proceed.
+ */
 public class JsResult {
-    // This prevents a user from interacting with the result before WebCore is
-    // ready to handle it.
-    private boolean mReady;
-    // Tells us if the user tried to confirm or cancel the result before WebCore
-    // is ready.
-    private boolean mTriedToNotifyBeforeReady;
     // This is a basic result of a confirm or prompt dialog.
     protected boolean mResult;
     /**
-     *  This is the caller of the prompt and is the object that is waiting.
-     *  @hide
+     * Callback interface, implemented by the WebViewProvider implementation to receive
+     * notifications when the JavaScript result represented by a JsResult instance has
+     * @hide Only for use by WebViewProvider implementations
      */
-    protected final CallbackProxy mProxy;
-    // This is the default value of the result.
-    private final boolean mDefaultValue;
+    public interface ResultReceiver {
+        public void onJsResultComplete(JsResult result);
+    }
+    /**
+     * This is the caller of the prompt and is the object that is waiting.
+     * @hide
+     */
+    protected final ResultReceiver mReceiver;
 
     /**
      * Handle the result if the user cancelled the dialog.
@@ -50,36 +54,22 @@
         wakeUp();
     }
 
-    /*package*/ JsResult(CallbackProxy proxy, boolean defaultVal) {
-        mProxy = proxy;
-        mDefaultValue = defaultVal;
+    /**
+     * @hide Only for use by WebViewProvider implementations
+     */
+    public JsResult(ResultReceiver receiver) {
+        mReceiver = receiver;
     }
 
-    /*package*/ final boolean getResult() {
+    /**
+     * @hide Only for use by WebViewProvider implementations
+     */
+    public final boolean getResult() {
         return mResult;
     }
 
-    /*package*/ final void setReady() {
-        mReady = true;
-        if (mTriedToNotifyBeforeReady) {
-            wakeUp();
-        }
-    }
-
-    /*package*/ void handleDefault() {
-        setReady();
-        mResult = mDefaultValue;
-        wakeUp();
-    }
-
-    /* Wake up the WebCore thread. */
+    /* Notify the caller that the JsResult has completed */
     protected final void wakeUp() {
-        if (mReady) {
-            synchronized (mProxy) {
-                mProxy.notify();
-            }
-        } else {
-            mTriedToNotifyBeforeReady = true;
-        }
+        mReceiver.onJsResultComplete(this);
     }
 }