Fix for bug 8607049: Improve javascript dialogs for classic
Make a common JsDialogHelper for classic and chromium webview Improve on before unload dialog Change-Id: Ib9711e4f95189ef8b2b31fc64aec7c0cb535d0e4
This commit is contained in:
@@ -17,10 +17,8 @@
|
||||
package android.webkit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
@@ -33,10 +31,6 @@ import android.os.SystemClock;
|
||||
import android.provider.Browser;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
@@ -92,10 +86,7 @@ class CallbackProxy extends Handler {
|
||||
private static final int CREATE_WINDOW = 109;
|
||||
private static final int CLOSE_WINDOW = 110;
|
||||
private static final int SAVE_PASSWORD = 111;
|
||||
private static final int JS_ALERT = 112;
|
||||
private static final int JS_CONFIRM = 113;
|
||||
private static final int JS_PROMPT = 114;
|
||||
private static final int JS_UNLOAD = 115;
|
||||
private static final int JS_DIALOG = 112;
|
||||
private static final int ASYNC_KEYEVENTS = 116;
|
||||
private static final int DOWNLOAD_FILE = 118;
|
||||
private static final int REPORT_ERROR = 119;
|
||||
@@ -566,188 +557,12 @@ class CallbackProxy extends Handler {
|
||||
}
|
||||
break;
|
||||
|
||||
case JS_ALERT:
|
||||
case JS_DIALOG:
|
||||
if (mWebChromeClient != null) {
|
||||
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();
|
||||
receiver.setReady();
|
||||
break;
|
||||
}
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle(getJsDialogTitle(url))
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int which) {
|
||||
res.confirm();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(
|
||||
DialogInterface dialog) {
|
||||
res.cancel();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
receiver.setReady();
|
||||
}
|
||||
break;
|
||||
|
||||
case JS_CONFIRM:
|
||||
if (mWebChromeClient != null) {
|
||||
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();
|
||||
receiver.setReady();
|
||||
break;
|
||||
}
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle(getJsDialogTitle(url))
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int which) {
|
||||
res.confirm();
|
||||
}})
|
||||
.setNegativeButton(R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int which) {
|
||||
res.cancel();
|
||||
}})
|
||||
.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(
|
||||
DialogInterface dialog) {
|
||||
res.cancel();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
// Tell the JsResult that it is ready for client
|
||||
// interaction.
|
||||
receiver.setReady();
|
||||
}
|
||||
break;
|
||||
|
||||
case JS_PROMPT:
|
||||
if (mWebChromeClient != null) {
|
||||
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");
|
||||
if (!mWebChromeClient.onJsPrompt(mWebView.getWebView(), url, message,
|
||||
defaultVal, res)) {
|
||||
if (!canShowAlertDialog()) {
|
||||
res.cancel();
|
||||
receiver.setReady();
|
||||
break;
|
||||
}
|
||||
final LayoutInflater factory = LayoutInflater
|
||||
.from(mContext);
|
||||
final View view = factory.inflate(R.layout.js_prompt,
|
||||
null);
|
||||
final EditText v = (EditText) view
|
||||
.findViewById(R.id.value);
|
||||
v.setText(defaultVal);
|
||||
((TextView) view.findViewById(R.id.message))
|
||||
.setText(message);
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle(getJsDialogTitle(url))
|
||||
.setView(view)
|
||||
.setPositiveButton(R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int whichButton) {
|
||||
res.confirm(v.getText()
|
||||
.toString());
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int whichButton) {
|
||||
res.cancel();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(
|
||||
DialogInterface dialog) {
|
||||
res.cancel();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
// Tell the JsResult that it is ready for client
|
||||
// interaction.
|
||||
receiver.setReady();
|
||||
}
|
||||
break;
|
||||
|
||||
case JS_UNLOAD:
|
||||
if (mWebChromeClient != null) {
|
||||
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();
|
||||
receiver.setReady();
|
||||
break;
|
||||
}
|
||||
final String m = mContext.getString(
|
||||
R.string.js_dialog_before_unload, message);
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setMessage(m)
|
||||
.setPositiveButton(R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int which) {
|
||||
res.confirm();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(
|
||||
DialogInterface dialog,
|
||||
int which) {
|
||||
res.cancel();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(
|
||||
DialogInterface dialog) {
|
||||
res.cancel();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
JsDialogHelper helper = new JsDialogHelper(receiver.mJsResult, msg);
|
||||
if (!helper.invokeCallback(mWebChromeClient, mWebView.getWebView())) {
|
||||
helper.showDialog(mContext);
|
||||
}
|
||||
receiver.setReady();
|
||||
}
|
||||
@@ -757,7 +572,7 @@ class CallbackProxy extends Handler {
|
||||
if(mWebChromeClient != null) {
|
||||
final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
|
||||
final JsResult res = receiver.mJsResult;
|
||||
if(mWebChromeClient.onJsTimeout()) {
|
||||
if (mWebChromeClient.onJsTimeout()) {
|
||||
res.confirm();
|
||||
} else {
|
||||
res.cancel();
|
||||
@@ -895,24 +710,6 @@ class CallbackProxy extends Handler {
|
||||
sendMessage(obtainMessage(SWITCH_OUT_HISTORY));
|
||||
}
|
||||
|
||||
private String getJsDialogTitle(String url) {
|
||||
String title = url;
|
||||
if (URLUtil.isDataUrl(url)) {
|
||||
// For data: urls, we just display 'JavaScript' similar to Safari.
|
||||
title = mContext.getString(R.string.js_dialog_title_default);
|
||||
} else {
|
||||
try {
|
||||
URL aUrl = new URL(url);
|
||||
// For example: "The page at 'http://www.mit.edu' says:"
|
||||
title = mContext.getString(R.string.js_dialog_title,
|
||||
aUrl.getProtocol() + "://" + aUrl.getHost());
|
||||
} catch (MalformedURLException ex) {
|
||||
// do nothing. just use the url as the title
|
||||
}
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// WebViewClient functions.
|
||||
// NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so
|
||||
@@ -1332,9 +1129,10 @@ class CallbackProxy extends Handler {
|
||||
return;
|
||||
}
|
||||
JsResultReceiver result = new JsResultReceiver();
|
||||
Message alert = obtainMessage(JS_ALERT, result);
|
||||
Message alert = obtainMessage(JS_DIALOG, result);
|
||||
alert.getData().putString("message", message);
|
||||
alert.getData().putString("url", url);
|
||||
alert.getData().putInt("type", JsDialogHelper.ALERT);
|
||||
sendMessageToUiThreadSync(alert);
|
||||
}
|
||||
|
||||
@@ -1345,9 +1143,10 @@ class CallbackProxy extends Handler {
|
||||
return false;
|
||||
}
|
||||
JsResultReceiver result = new JsResultReceiver();
|
||||
Message confirm = obtainMessage(JS_CONFIRM, result);
|
||||
Message confirm = obtainMessage(JS_DIALOG, result);
|
||||
confirm.getData().putString("message", message);
|
||||
confirm.getData().putString("url", url);
|
||||
confirm.getData().putInt("type", JsDialogHelper.CONFIRM);
|
||||
sendMessageToUiThreadSync(confirm);
|
||||
return result.mJsResult.getResult();
|
||||
}
|
||||
@@ -1359,10 +1158,11 @@ class CallbackProxy extends Handler {
|
||||
return null;
|
||||
}
|
||||
JsResultReceiver result = new JsResultReceiver();
|
||||
Message prompt = obtainMessage(JS_PROMPT, result);
|
||||
Message prompt = obtainMessage(JS_DIALOG, result);
|
||||
prompt.getData().putString("message", message);
|
||||
prompt.getData().putString("default", defaultValue);
|
||||
prompt.getData().putString("url", url);
|
||||
prompt.getData().putInt("type", JsDialogHelper.PROMPT);
|
||||
sendMessageToUiThreadSync(prompt);
|
||||
return result.mJsResult.getStringResult();
|
||||
}
|
||||
@@ -1374,10 +1174,11 @@ class CallbackProxy extends Handler {
|
||||
return true;
|
||||
}
|
||||
JsResultReceiver result = new JsResultReceiver();
|
||||
Message confirm = obtainMessage(JS_UNLOAD, result);
|
||||
confirm.getData().putString("message", message);
|
||||
confirm.getData().putString("url", url);
|
||||
sendMessageToUiThreadSync(confirm);
|
||||
Message unload = obtainMessage(JS_DIALOG, result);
|
||||
unload.getData().putString("message", message);
|
||||
unload.getData().putString("url", url);
|
||||
unload.getData().putInt("type", JsDialogHelper.UNLOAD);
|
||||
sendMessageToUiThreadSync(unload);
|
||||
return result.mJsResult.getResult();
|
||||
}
|
||||
|
||||
@@ -1595,16 +1396,6 @@ class CallbackProxy extends Handler {
|
||||
sendMessage(msg);
|
||||
}
|
||||
|
||||
boolean canShowAlertDialog() {
|
||||
// We can only display the alert dialog if mContext is
|
||||
// an Activity context.
|
||||
// FIXME: Should we display dialogs if mContext does
|
||||
// not have the window focus (e.g. if the user is viewing
|
||||
// another Activity when the alert should be displayed?
|
||||
// See bug 3166409
|
||||
return mContext instanceof Activity;
|
||||
}
|
||||
|
||||
private synchronized void sendMessageToUiThreadSync(Message msg) {
|
||||
sendMessage(msg);
|
||||
WebCoreThreadWatchdog.pause();
|
||||
|
||||
185
core/java/android/webkit/JsDialogHelper.java
Normal file
185
core/java/android/webkit/JsDialogHelper.java
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.webkit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Helper class to create JavaScript dialogs. It is used by
|
||||
* different WebView implementations.
|
||||
*
|
||||
* @hide Helper class for internal use
|
||||
*/
|
||||
public class JsDialogHelper {
|
||||
|
||||
private static final String TAG = "JsDialogHelper";
|
||||
|
||||
// Dialog types
|
||||
public static final int ALERT = 1;
|
||||
public static final int CONFIRM = 2;
|
||||
public static final int PROMPT = 3;
|
||||
public static final int UNLOAD = 4;
|
||||
|
||||
private final String mDefaultValue;
|
||||
private final JsPromptResult mResult;
|
||||
private final String mMessage;
|
||||
private final int mType;
|
||||
private final String mUrl;
|
||||
|
||||
public JsDialogHelper(JsPromptResult result, int type, String defaultValue, String message,
|
||||
String url) {
|
||||
mResult = result;
|
||||
mDefaultValue = defaultValue;
|
||||
mMessage = message;
|
||||
mType = type;
|
||||
mUrl = url;
|
||||
}
|
||||
|
||||
public JsDialogHelper(JsPromptResult result, Message msg) {
|
||||
mResult = result;
|
||||
mDefaultValue = msg.getData().getString("default");
|
||||
mMessage = msg.getData().getString("message");
|
||||
mType = msg.getData().getInt("type");
|
||||
mUrl = msg.getData().getString("url");
|
||||
}
|
||||
|
||||
public boolean invokeCallback(WebChromeClient client, WebView webView) {
|
||||
switch (mType) {
|
||||
case ALERT:
|
||||
return client.onJsAlert(webView, mUrl, mMessage, mResult);
|
||||
case CONFIRM:
|
||||
return client.onJsConfirm(webView, mUrl, mMessage, mResult);
|
||||
case UNLOAD:
|
||||
return client.onJsBeforeUnload(webView, mUrl, mMessage, mResult);
|
||||
case PROMPT:
|
||||
return client.onJsPrompt(webView, mUrl, mMessage, mDefaultValue, mResult);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected type: " + mType);
|
||||
}
|
||||
}
|
||||
|
||||
public void showDialog(Context context) {
|
||||
if (!canShowAlertDialog(context)) {
|
||||
Log.w(TAG, "Cannot create a dialog, the WebView context is not an Activity");
|
||||
mResult.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
String title, displayMessage;
|
||||
int positiveTextId, negativeTextId;
|
||||
if (mType == UNLOAD) {
|
||||
title = context.getString(com.android.internal.R.string.js_dialog_before_unload_title);
|
||||
displayMessage = context.getString(
|
||||
com.android.internal.R.string.js_dialog_before_unload, mMessage);
|
||||
positiveTextId = com.android.internal.R.string.js_dialog_before_unload_positive_button;
|
||||
negativeTextId = com.android.internal.R.string.js_dialog_before_unload_negative_button;
|
||||
} else {
|
||||
title = getJsDialogTitle(context);
|
||||
displayMessage = mMessage;
|
||||
positiveTextId = com.android.internal.R.string.ok;
|
||||
negativeTextId = com.android.internal.R.string.cancel;
|
||||
}
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(title);
|
||||
builder.setOnCancelListener(new CancelListener());
|
||||
if (mType != PROMPT) {
|
||||
builder.setMessage(displayMessage);
|
||||
builder.setPositiveButton(positiveTextId, new PositiveListener(null));
|
||||
} else {
|
||||
final View view = LayoutInflater.from(context).inflate(
|
||||
com.android.internal.R.layout.js_prompt, null);
|
||||
EditText edit = ((EditText) view.findViewById(com.android.internal.R.id.value));
|
||||
edit.setText(mDefaultValue);
|
||||
builder.setPositiveButton(positiveTextId, new PositiveListener(edit));
|
||||
((TextView) view.findViewById(com.android.internal.R.id.message)).setText(mMessage);
|
||||
builder.setView(view);
|
||||
}
|
||||
if (mType != ALERT) {
|
||||
builder.setNegativeButton(negativeTextId, new CancelListener());
|
||||
}
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private class CancelListener implements DialogInterface.OnCancelListener,
|
||||
DialogInterface.OnClickListener {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
mResult.cancel();
|
||||
}
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mResult.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private class PositiveListener implements DialogInterface.OnClickListener {
|
||||
private final EditText mEdit;
|
||||
|
||||
public PositiveListener(EditText edit) {
|
||||
mEdit = edit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (mEdit == null) {
|
||||
mResult.confirm();
|
||||
} else {
|
||||
mResult.confirm(mEdit.getText().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getJsDialogTitle(Context context) {
|
||||
String title = mUrl;
|
||||
if (URLUtil.isDataUrl(mUrl)) {
|
||||
// For data: urls, we just display 'JavaScript' similar to Chrome.
|
||||
title = context.getString(com.android.internal.R.string.js_dialog_title_default);
|
||||
} else {
|
||||
try {
|
||||
URL alertUrl = new URL(mUrl);
|
||||
// For example: "The page at 'http://www.mit.edu' says:"
|
||||
title = context.getString(com.android.internal.R.string.js_dialog_title,
|
||||
alertUrl.getProtocol() + "://" + alertUrl.getHost());
|
||||
} catch (MalformedURLException ex) {
|
||||
// do nothing. just use the url as the title
|
||||
}
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
private static boolean canShowAlertDialog(Context context) {
|
||||
// We can only display the alert dialog if mContext is
|
||||
// an Activity context.
|
||||
// FIXME: Should we display dialogs if mContext does
|
||||
// not have the window focus (e.g. if the user is viewing
|
||||
// another Activity when the alert should be displayed) ?
|
||||
// See bug 3166409
|
||||
return context instanceof Activity;
|
||||
}
|
||||
}
|
||||
@@ -2406,9 +2406,14 @@
|
||||
<string name="js_dialog_title">The page at \"<xliff:g id="title">%s</xliff:g>\" says:</string>
|
||||
<!-- Default title for a javascript dialog -->
|
||||
<string name="js_dialog_title_default">JavaScript</string>
|
||||
<!-- Message in a javascript dialog asking if the user wishes to leave the
|
||||
current page -->
|
||||
<string name="js_dialog_before_unload">Navigate away from this page?\n\n<xliff:g id="message">%s</xliff:g>\n\nTouch OK to continue, or Cancel to stay on the current page.</string>
|
||||
<!-- Title for the unload javascript dialog -->
|
||||
<string name="js_dialog_before_unload_title">Confirm Navigation</string>
|
||||
<!-- Text for the positive button on the unload javascript dialog -->
|
||||
<string name="js_dialog_before_unload_positive_button">Leave this Page</string>
|
||||
<!-- Text for the negative button on the unload javascript dialog -->
|
||||
<string name="js_dialog_before_unload_negative_button">Stay on this Page</string>
|
||||
<!-- Message in a javascript dialog asking if the user wishes to leave the current page -->
|
||||
<string name="js_dialog_before_unload"><xliff:g id="message">%s</xliff:g>\n\nAre you sure you want to navigate away from this page?</string>
|
||||
|
||||
<!-- Title of the WebView save password dialog. If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. -->
|
||||
<string name="save_password_label">Confirm</string>
|
||||
|
||||
@@ -549,6 +549,9 @@
|
||||
<java-symbol type="string" name="ime_action_search" />
|
||||
<java-symbol type="string" name="ime_action_send" />
|
||||
<java-symbol type="string" name="invalidPin" />
|
||||
<java-symbol type="string" name="js_dialog_before_unload_positive_button" />
|
||||
<java-symbol type="string" name="js_dialog_before_unload_negative_button" />
|
||||
<java-symbol type="string" name="js_dialog_before_unload_title" />
|
||||
<java-symbol type="string" name="js_dialog_before_unload" />
|
||||
<java-symbol type="string" name="js_dialog_title" />
|
||||
<java-symbol type="string" name="js_dialog_title_default" />
|
||||
|
||||
Reference in New Issue
Block a user