Commit 23c8f28b authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[TabModal] Fix metrics dismissal cause for JavaScript dialog

+ Record dialog dismissal on Android side as kDialogClosed
+ Add metrics recording support for modal dialogs, and remove onCancel
  callback

Bug: 873236
Change-Id: Idbe990f2c70883f851bf5214dbf0a3e7b96f4260
Reviewed-on: https://chromium-review.googlesource.com/1222990Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#593634}
parent c79cc2cb
...@@ -20,6 +20,7 @@ import org.chromium.base.VisibleForTesting; ...@@ -20,6 +20,7 @@ import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.jsdialog.JavascriptModalDialogView; import org.chromium.chrome.browser.jsdialog.JavascriptModalDialogView;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.vr.VrModuleProvider; import org.chromium.chrome.browser.vr.VrModuleProvider;
...@@ -151,12 +152,12 @@ public class JavascriptAppModalDialog ...@@ -151,12 +152,12 @@ public class JavascriptAppModalDialog
public void onClick(@ModalDialogView.ButtonType int buttonType) { public void onClick(@ModalDialogView.ButtonType int buttonType) {
switch (buttonType) { switch (buttonType) {
case ModalDialogView.ButtonType.POSITIVE: case ModalDialogView.ButtonType.POSITIVE:
confirm(mDialogView.getPromptText(), false); mModalDialogManager.dismissDialog(
mModalDialogManager.dismissDialog(mDialogView); mDialogView, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
break; break;
case ModalDialogView.ButtonType.NEGATIVE: case ModalDialogView.ButtonType.NEGATIVE:
cancel(false); mModalDialogManager.dismissDialog(
mModalDialogManager.dismissDialog(mDialogView); mDialogView, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
break; break;
default: default:
Log.e(TAG, "Unexpected button pressed in dialog: " + buttonType); Log.e(TAG, "Unexpected button pressed in dialog: " + buttonType);
...@@ -164,13 +165,21 @@ public class JavascriptAppModalDialog ...@@ -164,13 +165,21 @@ public class JavascriptAppModalDialog
} }
@Override @Override
public void onCancel() { public void onDismiss(@DialogDismissalCause int dismissalCause) {
cancel(false); // TODO(https://crbug.com/874537): Add suppression logic in the refactor and make sure it
// doesn't break VR.
switch (dismissalCause) {
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
confirm(mDialogView.getPromptText(), false);
break;
case DialogDismissalCause.DISMISSED_BY_NATIVE:
break;
default:
cancel(false);
}
mDialogView = null;
} }
@Override
public void onDismiss() {}
protected void prepare(final ViewGroup layout) { protected void prepare(final ViewGroup layout) {
// Display the checkbox for suppressing dialogs if necessary. // Display the checkbox for suppressing dialogs if necessary.
layout.findViewById(R.id.suppress_js_modal_dialogs).setVisibility( layout.findViewById(R.id.suppress_js_modal_dialogs).setVisibility(
...@@ -213,7 +222,8 @@ public class JavascriptAppModalDialog ...@@ -213,7 +222,8 @@ public class JavascriptAppModalDialog
if (mDialog != null) { if (mDialog != null) {
mDialog.dismiss(); mDialog.dismiss();
} else { } else {
mModalDialogManager.dismissDialog(mDialogView); mModalDialogManager.dismissDialog(
mDialogView, DialogDismissalCause.DISMISSED_BY_NATIVE);
} }
mNativeDialogPointer = 0; mNativeDialogPointer = 0;
} }
......
...@@ -39,6 +39,7 @@ import org.chromium.base.VisibleForTesting; ...@@ -39,6 +39,7 @@ import org.chromium.base.VisibleForTesting;
import org.chromium.base.task.AsyncTask; import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
...@@ -746,15 +747,13 @@ public class CardUnmaskPrompt implements TextWatcher, OnClickListener, ModalDial ...@@ -746,15 +747,13 @@ public class CardUnmaskPrompt implements TextWatcher, OnClickListener, ModalDial
mMonthInput.getText().toString(), Integer.toString(getFourDigitYear()), mMonthInput.getText().toString(), Integer.toString(getFourDigitYear()),
mStoreLocallyCheckbox != null && mStoreLocallyCheckbox.isChecked()); mStoreLocallyCheckbox != null && mStoreLocallyCheckbox.isChecked());
} else if (buttonType == ModalDialogView.ButtonType.NEGATIVE) { } else if (buttonType == ModalDialogView.ButtonType.NEGATIVE) {
mModalDialogManager.cancelDialog(mDialog); mModalDialogManager.dismissDialog(
mDialog, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
} }
} }
@Override @Override
public void onCancel() {} public void onDismiss(@DialogDismissalCause int dismissalCause) {
@Override
public void onDismiss() {
mDelegate.dismissed(); mDelegate.dismissed();
} }
......
...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.download; ...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.download;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.preferences.PrefServiceBridge;
...@@ -34,7 +35,10 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller ...@@ -34,7 +35,10 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller
@CalledByNative @CalledByNative
private void destroy() { private void destroy() {
mNativeDownloadLocationDialogBridge = 0; mNativeDownloadLocationDialogBridge = 0;
if (mModalDialogManager != null) mModalDialogManager.dismissDialog(mLocationDialog); if (mModalDialogManager != null) {
mModalDialogManager.dismissDialog(
mLocationDialog, DialogDismissalCause.DISMISSED_BY_NATIVE);
}
} }
@CalledByNative @CalledByNative
...@@ -43,7 +47,7 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller ...@@ -43,7 +47,7 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller
ChromeActivity activity = (ChromeActivity) windowAndroid.getActivity().get(); ChromeActivity activity = (ChromeActivity) windowAndroid.getActivity().get();
// If the activity has gone away, just clean up the native pointer. // If the activity has gone away, just clean up the native pointer.
if (activity == null) { if (activity == null) {
onCancel(); onDismiss(DialogDismissalCause.ACTIVITY_DESTROYED);
return; return;
} }
...@@ -60,29 +64,33 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller ...@@ -60,29 +64,33 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller
public void onClick(@ModalDialogView.ButtonType int buttonType) { public void onClick(@ModalDialogView.ButtonType int buttonType) {
switch (buttonType) { switch (buttonType) {
case ModalDialogView.ButtonType.POSITIVE: case ModalDialogView.ButtonType.POSITIVE:
handleResponses(mLocationDialog.getFileName(), mLocationDialog.getDirectoryOption(), mModalDialogManager.dismissDialog(
mLocationDialog.getDontShowAgain()); mLocationDialog, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
mModalDialogManager.dismissDialog(mLocationDialog);
break; break;
case ModalDialogView.ButtonType.NEGATIVE: case ModalDialogView.ButtonType.NEGATIVE:
// Intentional fall-through. mModalDialogManager.dismissDialog(
mLocationDialog, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
break;
default: default:
cancel();
mModalDialogManager.dismissDialog(mLocationDialog);
} }
mLocationDialog = null; mLocationDialog = null;
} }
@Override @Override
public void onCancel() { public void onDismiss(@DialogDismissalCause int dismissalCause) {
cancel(); switch (dismissalCause) {
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
handleResponses(mLocationDialog.getFileName(), mLocationDialog.getDirectoryOption(),
mLocationDialog.getDontShowAgain());
break;
default:
cancel();
break;
}
mLocationDialog = null; mLocationDialog = null;
} }
@Override
public void onDismiss() {}
/** /**
* Pass along information from location dialog to native. * Pass along information from location dialog to native.
* *
......
...@@ -15,6 +15,7 @@ import android.widget.CheckBox; ...@@ -15,6 +15,7 @@ import android.widget.CheckBox;
import org.chromium.base.task.AsyncTask; import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.modaldialog.AppModalPresenter; import org.chromium.chrome.browser.modaldialog.AppModalPresenter;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager.ModalDialogType; import org.chromium.chrome.browser.modaldialog.ModalDialogManager.ModalDialogType;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
...@@ -86,12 +87,7 @@ public class IncognitoDisclosureActivity extends AppCompatActivity { ...@@ -86,12 +87,7 @@ public class IncognitoDisclosureActivity extends AppCompatActivity {
} }
@Override @Override
public void onCancel() { public void onDismiss(@DialogDismissalCause int dismissalCause) {
finish();
}
@Override
public void onDismiss() {
finish(); finish();
} }
}; };
......
...@@ -8,6 +8,7 @@ import org.chromium.base.Log; ...@@ -8,6 +8,7 @@ import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
...@@ -70,7 +71,7 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller { ...@@ -70,7 +71,7 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller {
ChromeActivity activity = (ChromeActivity) window.getActivity().get(); ChromeActivity activity = (ChromeActivity) window.getActivity().get();
// If the activity has gone away, then just clean up the native pointer. // If the activity has gone away, then just clean up the native pointer.
if (activity == null) { if (activity == null) {
nativeCancel(nativeDialogPointer); nativeCancel(nativeDialogPointer, false);
return; return;
} }
...@@ -90,7 +91,7 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller { ...@@ -90,7 +91,7 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller {
@CalledByNative @CalledByNative
private void dismiss() { private void dismiss() {
mModalDialogManager.dismissDialog(mDialogView); mModalDialogManager.dismissDialog(mDialogView, DialogDismissalCause.DISMISSED_BY_NATIVE);
mNativeDialogPointer = 0; mNativeDialogPointer = 0;
} }
...@@ -98,12 +99,12 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller { ...@@ -98,12 +99,12 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller {
public void onClick(@ModalDialogView.ButtonType int buttonType) { public void onClick(@ModalDialogView.ButtonType int buttonType) {
switch (buttonType) { switch (buttonType) {
case ModalDialogView.ButtonType.POSITIVE: case ModalDialogView.ButtonType.POSITIVE:
accept(mDialogView.getPromptText()); mModalDialogManager.dismissDialog(
mModalDialogManager.dismissDialog(mDialogView); mDialogView, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
break; break;
case ModalDialogView.ButtonType.NEGATIVE: case ModalDialogView.ButtonType.NEGATIVE:
cancel(); mModalDialogManager.dismissDialog(
mModalDialogManager.dismissDialog(mDialogView); mDialogView, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
break; break;
default: default:
Log.e(TAG, "Unexpected button pressed in dialog: " + buttonType); Log.e(TAG, "Unexpected button pressed in dialog: " + buttonType);
...@@ -111,32 +112,40 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller { ...@@ -111,32 +112,40 @@ public class JavascriptTabModalDialog implements ModalDialogView.Controller {
} }
@Override @Override
public void onCancel() { public void onDismiss(@DialogDismissalCause int dismissalCause) {
cancel(); switch (dismissalCause) {
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
accept(mDialogView.getPromptText());
break;
case DialogDismissalCause.NEGATIVE_BUTTON_CLICKED:
cancel(true);
break;
case DialogDismissalCause.DISMISSED_BY_NATIVE:
// We don't need to call native back in this case.
break;
default:
cancel(false);
}
mDialogView = null;
} }
@Override
public void onDismiss() {}
/** /**
* Sends notification to native that the user accepts the dialog. * Sends notification to native that the user accepts the dialog.
* @param promptResult The text edited by user. * @param promptResult The text edited by user.
*/ */
private void accept(String promptResult) { private void accept(String promptResult) {
if (mNativeDialogPointer != 0) { if (mNativeDialogPointer == 0) return;
nativeAccept(mNativeDialogPointer, promptResult); nativeAccept(mNativeDialogPointer, promptResult);
}
} }
/** /**
* Sends notification to native that the user cancels the dialog. * Sends notification to native that the user cancels the dialog.
*/ */
private void cancel() { private void cancel(boolean buttonClicked) {
if (mNativeDialogPointer != 0) { if (mNativeDialogPointer == 0) return;
nativeCancel(mNativeDialogPointer); nativeCancel(mNativeDialogPointer, buttonClicked);
}
} }
private native void nativeAccept(long nativeJavaScriptDialogAndroid, String prompt); private native void nativeAccept(long nativeJavaScriptDialogAndroid, String prompt);
private native void nativeCancel(long nativeJavaScriptDialogAndroid); private native void nativeCancel(long nativeJavaScriptDialogAndroid, boolean buttonClicked);
} }
...@@ -18,6 +18,7 @@ import android.widget.TextView; ...@@ -18,6 +18,7 @@ import android.widget.TextView;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.preferences.PrefServiceBridge;
...@@ -225,16 +226,15 @@ public class LanguageAskPrompt implements ModalDialogView.Controller { ...@@ -225,16 +226,15 @@ public class LanguageAskPrompt implements ModalDialogView.Controller {
@Override @Override
public void onClick(int buttonType) { public void onClick(int buttonType) {
if (buttonType == ModalDialogView.ButtonType.NEGATIVE) { if (buttonType == ModalDialogView.ButtonType.NEGATIVE) {
mModalDialogManager.cancelDialog(mDialog); mModalDialogManager.dismissDialog(
mDialog, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
} else { } else {
saveLanguages(); saveLanguages();
mModalDialogManager.dismissDialog(mDialog); mModalDialogManager.dismissDialog(
mDialog, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
} }
} }
@Override @Override
public void onCancel() {} public void onDismiss(@DialogDismissalCause int dismissalCause) {}
@Override
public void onDismiss() {}
} }
...@@ -26,7 +26,8 @@ public class AppModalPresenter extends ModalDialogManager.Presenter { ...@@ -26,7 +26,8 @@ public class AppModalPresenter extends ModalDialogManager.Presenter {
@Override @Override
protected void addDialogView(View dialogView) { protected void addDialogView(View dialogView) {
mDialog = new Dialog(mContext, R.style.ModalDialogTheme); mDialog = new Dialog(mContext, R.style.ModalDialogTheme);
mDialog.setOnCancelListener(dialogInterface -> cancelCurrentDialog()); mDialog.setOnCancelListener(dialogInterface
-> dismissCurrentDialog(DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE));
ViewGroup container = (ViewGroup) LayoutInflater.from(mContext).inflate( ViewGroup container = (ViewGroup) LayoutInflater.from(mContext).inflate(
R.layout.modal_dialog_container, null); R.layout.modal_dialog_container, null);
// We use the Android Dialog dim for app modal dialog, so a custom scrim is not needed. // We use the Android Dialog dim for app modal dialog, so a custom scrim is not needed.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.modaldialog;
import android.support.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@IntDef({DialogDismissalCause.UNKNOWN, DialogDismissalCause.POSITIVE_BUTTON_CLICKED,
DialogDismissalCause.NEGATIVE_BUTTON_CLICKED, DialogDismissalCause.ACTION_ON_CONTENT,
DialogDismissalCause.DISMISSED_BY_NATIVE,
DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE, DialogDismissalCause.TAB_SWITCHED,
DialogDismissalCause.TAB_DESTROYED, DialogDismissalCause.ACTIVITY_DESTROYED})
@Retention(RetentionPolicy.SOURCE)
public @interface DialogDismissalCause {
// Please do not remove or change the order of the existing values, and add new value at the end
// of the enum. Dismissal causes that are fully controlled by clients (i.e. are not used inside
// the dialog manager or the dialog presenters) are marked "Controlled by client" on comments.
/** No specified reason for the dialog dismissal. */
int UNKNOWN = 0;
/** Controlled by client: Positive button (e.g. OK button) is clicked by the user. */
int POSITIVE_BUTTON_CLICKED = 1;
/** Controlled by client: Negative button (e.g. Cancel button) is clicked by the user. */
int NEGATIVE_BUTTON_CLICKED = 2;
/** Controlled by client: Action taken on the dialog content triggers the dialog dismissal. */
int ACTION_ON_CONTENT = 3;
/** Controlled by client: Dialog is dismissed by native c++ objects. */
int DISMISSED_BY_NATIVE = 4;
/** User clicks the navigate back button or touches the scrim outside the dialog. */
int NAVIGATE_BACK_OR_TOUCH_OUTSIDE = 5;
/** User switches away the tab associated with the dialog. */
int TAB_SWITCHED = 6;
/** The Tab associated with the dialog is destroyed. */
int TAB_DESTROYED = 7;
/** The activity associated with the dialog is destroyed. */
int ACTIVITY_DESTROYED = 8;
}
...@@ -10,6 +10,7 @@ import android.support.annotation.Nullable; ...@@ -10,6 +10,7 @@ import android.support.annotation.Nullable;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.View; import android.view.View;
import org.chromium.base.Callback;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.ui.UiUtils; import org.chromium.ui.UiUtils;
...@@ -28,7 +29,7 @@ public class ModalDialogManager { ...@@ -28,7 +29,7 @@ public class ModalDialogManager {
* Present a {@link ModalDialogView} in a container. * Present a {@link ModalDialogView} in a container.
*/ */
public static abstract class Presenter { public static abstract class Presenter {
private Runnable mCancelCallback; private Callback<Integer> mDismissCallback;
private ModalDialogView mModalDialog; private ModalDialogView mModalDialog;
private View mCurrentView; private View mCurrentView;
...@@ -37,17 +38,17 @@ public class ModalDialogManager { ...@@ -37,17 +38,17 @@ public class ModalDialogManager {
* is currently showing. * is currently showing.
*/ */
private void setModalDialog( private void setModalDialog(
@Nullable ModalDialogView dialog, @Nullable Runnable cancelCallback) { @Nullable ModalDialogView dialog, @Nullable Callback<Integer> dismissCallback) {
if (dialog == null) { if (dialog == null) {
removeDialogView(mCurrentView); removeDialogView(mCurrentView);
mModalDialog = null; mModalDialog = null;
mCancelCallback = null; mDismissCallback = null;
} else { } else {
assert mModalDialog assert mModalDialog
== null : "Should call setModalDialog(null) before setting a modal dialog."; == null : "Should call setModalDialog(null) before setting a modal dialog.";
mModalDialog = dialog; mModalDialog = dialog;
mCurrentView = dialog.getView(); mCurrentView = dialog.getView();
mCancelCallback = cancelCallback; mDismissCallback = dismissCallback;
// Make sure the view is detached from any parent before adding it to the container. // Make sure the view is detached from any parent before adding it to the container.
// This is not detached after removeDialogView() because there could be animation // This is not detached after removeDialogView() because there could be animation
// running on removing the dialog view. // running on removing the dialog view.
...@@ -59,14 +60,14 @@ public class ModalDialogManager { ...@@ -59,14 +60,14 @@ public class ModalDialogManager {
/** /**
* Run the cached cancel callback and reset the cached callback. * Run the cached cancel callback and reset the cached callback.
*/ */
protected final void cancelCurrentDialog() { protected final void dismissCurrentDialog(@DialogDismissalCause int dismissalCause) {
if (mCancelCallback == null) return; if (mDismissCallback == null) return;
// Set #mCancelCallback to null before calling the callback to avoid it being // Set #mCancelCallback to null before calling the callback to avoid it being
// updated during the callback. // updated during the callback.
Runnable callback = mCancelCallback; Callback<Integer> callback = mDismissCallback;
mCancelCallback = null; mDismissCallback = null;
callback.run(); callback.onResult(dismissalCause);
} }
/** /**
...@@ -143,7 +144,7 @@ public class ModalDialogManager { ...@@ -143,7 +144,7 @@ public class ModalDialogManager {
/** Clears any dependencies on the showing or pending dialogs. */ /** Clears any dependencies on the showing or pending dialogs. */
public void destroy() { public void destroy() {
cancelAllDialogs(); dismissAllDialogs(DialogDismissalCause.ACTIVITY_DESTROYED);
} }
/** /**
...@@ -201,7 +202,8 @@ public class ModalDialogManager { ...@@ -201,7 +202,8 @@ public class ModalDialogManager {
dialog.prepareBeforeShow(); dialog.prepareBeforeShow();
mCurrentType = dialogType; mCurrentType = dialogType;
mCurrentPresenter = mPresenters.get(dialogType, mDefaultPresenter); mCurrentPresenter = mPresenters.get(dialogType, mDefaultPresenter);
mCurrentPresenter.setModalDialog(dialog, () -> cancelDialog(dialog)); mCurrentPresenter.setModalDialog(
dialog, (dismissalCause) -> dismissDialog(dialog, dismissalCause));
} }
/** /**
...@@ -209,14 +211,29 @@ public class ModalDialogManager { ...@@ -209,14 +211,29 @@ public class ModalDialogManager {
* the pending dialog list. If the dialog is currently being dismissed this function does * the pending dialog list. If the dialog is currently being dismissed this function does
* nothing. * nothing.
* @param dialog The dialog to be dismissed or removed from pending list. * @param dialog The dialog to be dismissed or removed from pending list.
*
* TODO(huayinz): Remove this method and use the one with dismissal cause.
*/ */
public void dismissDialog(ModalDialogView dialog) { public void dismissDialog(ModalDialogView dialog) {
dismissDialog(dialog, DialogDismissalCause.UNKNOWN);
}
/**
* Dismiss the specified dialog. If the dialog is not currently showing, it will be removed from
* the pending dialog list. If the dialog is currently being dismissed this function does
* nothing.
* @param dialog The dialog to be dismissed or removed from pending list.
* @param dismissalCause The {@link DialogDismissalCause} that describes why the dialog is
* dismissed.
*/
public void dismissDialog(ModalDialogView dialog, @DialogDismissalCause int dismissalCause) {
if (dialog == null) return;
if (mCurrentPresenter == null || dialog != mCurrentPresenter.getModalDialog()) { if (mCurrentPresenter == null || dialog != mCurrentPresenter.getModalDialog()) {
for (int i = 0; i < mPendingDialogs.size(); ++i) { for (int i = 0; i < mPendingDialogs.size(); ++i) {
List<ModalDialogView> dialogs = mPendingDialogs.valueAt(i); List<ModalDialogView> dialogs = mPendingDialogs.valueAt(i);
for (int j = 0; j < dialogs.size(); ++j) { for (int j = 0; j < dialogs.size(); ++j) {
if (dialogs.get(j) == dialog) { if (dialogs.get(j) == dialog) {
dialogs.remove(j).getController().onDismiss(); dialogs.remove(j).getController().onDismiss(dismissalCause);
return; return;
} }
} }
...@@ -229,7 +246,7 @@ public class ModalDialogManager { ...@@ -229,7 +246,7 @@ public class ModalDialogManager {
assert dialog == mCurrentPresenter.getModalDialog(); assert dialog == mCurrentPresenter.getModalDialog();
if (mDismissingCurrentDialog) return; if (mDismissingCurrentDialog) return;
mDismissingCurrentDialog = true; mDismissingCurrentDialog = true;
dialog.getController().onDismiss(); dialog.getController().onDismiss(dismissalCause);
mCurrentPresenter.setModalDialog(null, null); mCurrentPresenter.setModalDialog(null, null);
mCurrentPresenter = null; mCurrentPresenter = null;
mDismissingCurrentDialog = false; mDismissingCurrentDialog = false;
...@@ -237,48 +254,40 @@ public class ModalDialogManager { ...@@ -237,48 +254,40 @@ public class ModalDialogManager {
} }
/** /**
* Cancel showing the specified dialog. This is essentially the same as * Dismiss the dialog currently shown and remove all pending dialogs.
* {@link #dismissDialog(ModalDialogView)} but will also call the onCancelled callback from the * @param dismissalCause The {@link DialogDismissalCause} that describes why the dialogs are
* modal dialog. * dismissed.
* @param dialog The dialog to be cancelled.
*/
public void cancelDialog(ModalDialogView dialog) {
dialog.getController().onCancel();
dismissDialog(dialog);
}
/**
* Dismiss the dialog currently shown and remove all pending dialogs and call the onCancelled
* callbacks from the modal dialogs.
*/ */
public void cancelAllDialogs() { public void dismissAllDialogs(@DialogDismissalCause int dismissalCause) {
for (int i = 0; i < mPendingDialogs.size(); ++i) { for (int i = 0; i < mPendingDialogs.size(); ++i) {
cancelPendingDialogs(mPendingDialogs.keyAt(i)); dismissPendingDialogsOfType(mPendingDialogs.keyAt(i), dismissalCause);
} }
if (isShowing()) cancelDialog(mCurrentPresenter.getModalDialog()); if (isShowing()) dismissDialog(mCurrentPresenter.getModalDialog());
} }
/** /**
* Dismiss the dialog currently shown and remove all pending dialogs of the specified type and * Dismiss the dialog currently shown and remove all pending dialogs of the specified type.
* call the onCancelled callbacks from the modal dialogs.
* @param dialogType The specified type of dialog. * @param dialogType The specified type of dialog.
* @param dismissalCause The {@link DialogDismissalCause} that describes why the dialogs are
* dismissed.
*/ */
protected void cancelAllDialogs(@ModalDialogType int dialogType) { protected void dismissDialogsOfType(
cancelPendingDialogs(dialogType); @ModalDialogType int dialogType, @DialogDismissalCause int dismissalCause) {
dismissPendingDialogsOfType(dialogType, dismissalCause);
if (isShowing() && dialogType == mCurrentType) { if (isShowing() && dialogType == mCurrentType) {
cancelDialog(mCurrentPresenter.getModalDialog()); dismissDialog(mCurrentPresenter.getModalDialog(), dismissalCause);
} }
} }
/** Helper method to cancel pending dialogs of the specified type. */ /** Helper method to dismiss pending dialogs of the specified type. */
private void cancelPendingDialogs(@ModalDialogType int dialogType) { private void dismissPendingDialogsOfType(
@ModalDialogType int dialogType, @DialogDismissalCause int dismissalCause) {
List<ModalDialogView> dialogs = mPendingDialogs.get(dialogType); List<ModalDialogView> dialogs = mPendingDialogs.get(dialogType);
if (dialogs == null) return; if (dialogs == null) return;
while (!dialogs.isEmpty()) { while (!dialogs.isEmpty()) {
ModalDialogView.Controller controller = dialogs.remove(0).getController(); ModalDialogView.Controller controller = dialogs.remove(0).getController();
controller.onDismiss(); controller.onDismiss(dismissalCause);
controller.onCancel();
} }
} }
......
...@@ -40,15 +40,16 @@ public class ModalDialogView implements View.OnClickListener { ...@@ -40,15 +40,16 @@ public class ModalDialogView implements View.OnClickListener {
void onClick(@ButtonType int buttonType); void onClick(@ButtonType int buttonType);
/** /**
* Handle cancel event when the dialog is not dismissed by actions on the dialog such as * Handle dismiss event when the dialog is dismissed by actions on the dialog. Note that it
* back press, and on tab modal dialog, tab switcher button click. * can be dangerous to the {@code dismissalCause} for business logic other than metrics
* recording, unless the dismissal cause is fully controlled by the client (e.g. button
* clicked), because the dismissal cause can be different values depending on modal dialog
* type and mode of presentation (e.g. it could be unknown on VR but a specific value on
* non-VR).
* @param dismissalCause The reason of the dialog being dismissed.
* @see DialogDismissalCause
*/ */
void onCancel(); void onDismiss(@DialogDismissalCause int dismissalCause);
/**
* Handle dismiss event when the dialog is dismissed by actions on the dialog.
*/
void onDismiss();
} }
/** Parameters that can be used to create a new ModalDialogView. */ /** Parameters that can be used to create a new ModalDialogView. */
......
...@@ -28,7 +28,8 @@ public class TabModalLifetimeHandler { ...@@ -28,7 +28,8 @@ public class TabModalLifetimeHandler {
@Override @Override
public void onDestroyed(Tab tab) { public void onDestroyed(Tab tab) {
if (mActiveTab == tab) { if (mActiveTab == tab) {
mManager.cancelAllDialogs(ModalDialogType.TAB); mManager.dismissDialogsOfType(
ModalDialogType.TAB, DialogDismissalCause.TAB_DESTROYED);
mActiveTab = null; mActiveTab = null;
} }
} }
...@@ -58,7 +59,8 @@ public class TabModalLifetimeHandler { ...@@ -58,7 +59,8 @@ public class TabModalLifetimeHandler {
// Do not use lastId here since it can be the selected tab's ID if model is switched // Do not use lastId here since it can be the selected tab's ID if model is switched
// inside tab switcher. // inside tab switcher.
if (tab != mActiveTab) { if (tab != mActiveTab) {
mManager.cancelAllDialogs(ModalDialogType.TAB); mManager.dismissDialogsOfType(
ModalDialogType.TAB, DialogDismissalCause.TAB_SWITCHED);
if (mActiveTab != null) mActiveTab.removeObserver(mTabObserver); if (mActiveTab != null) mActiveTab.removeObserver(mTabObserver);
mActiveTab = tab; mActiveTab = tab;
...@@ -87,7 +89,7 @@ public class TabModalLifetimeHandler { ...@@ -87,7 +89,7 @@ public class TabModalLifetimeHandler {
*/ */
public boolean handleBackPress() { public boolean handleBackPress() {
if (mPresenter.getModalDialog() == null) return false; if (mPresenter.getModalDialog() == null) return false;
mPresenter.cancelCurrentDialog(); mPresenter.dismissCurrentDialog(DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE);
return true; return true;
} }
......
...@@ -24,6 +24,7 @@ import org.chromium.base.Log; ...@@ -24,6 +24,7 @@ import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ResourceId; import org.chromium.chrome.browser.ResourceId;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.modaldialog.ModalDialogView.ButtonType; import org.chromium.chrome.browser.modaldialog.ModalDialogView.ButtonType;
...@@ -231,10 +232,7 @@ public class ConnectionInfoPopup implements OnClickListener, ModalDialogView.Con ...@@ -231,10 +232,7 @@ public class ConnectionInfoPopup implements OnClickListener, ModalDialogView.Con
public void onClick(@ButtonType int buttonType) {} public void onClick(@ButtonType int buttonType) {}
@Override @Override
public void onCancel() {} public void onDismiss(@DialogDismissalCause int dismissalCause) {
@Override
public void onDismiss() {
assert mNativeConnectionInfoPopup != 0; assert mNativeConnectionInfoPopup != 0;
mWebContentsObserver.destroy(); mWebContentsObserver.destroy();
nativeDestroy(mNativeConnectionInfoPopup); nativeDestroy(mNativeConnectionInfoPopup);
......
...@@ -31,6 +31,7 @@ import org.chromium.base.metrics.RecordUserAction; ...@@ -31,6 +31,7 @@ import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.instantapps.InstantAppsHandler; import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.modaldialog.ModalDialogView.ButtonType; import org.chromium.chrome.browser.modaldialog.ModalDialogView.ButtonType;
import org.chromium.chrome.browser.offlinepages.OfflinePageItem; import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
...@@ -479,10 +480,7 @@ public class PageInfoController ...@@ -479,10 +480,7 @@ public class PageInfoController
public void onClick(@ButtonType int buttonType) {} public void onClick(@ButtonType int buttonType) {}
@Override @Override
public void onCancel() {} public void onDismiss(@DialogDismissalCause int dismissalCause) {
@Override
public void onDismiss() {
assert mNativePageInfoController != 0; assert mNativePageInfoController != 0;
if (mPendingRunAfterDismissTask != null) { if (mPendingRunAfterDismissTask != null) {
mPendingRunAfterDismissTask.run(); mPendingRunAfterDismissTask.run();
......
...@@ -21,6 +21,7 @@ import android.view.Window; ...@@ -21,6 +21,7 @@ import android.view.Window;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ScrollView; import android.widget.ScrollView;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.ui.interpolators.BakedBezierInterpolator; import org.chromium.ui.interpolators.BakedBezierInterpolator;
...@@ -157,7 +158,7 @@ class PageInfoDialog { ...@@ -157,7 +158,7 @@ class PageInfoDialog {
sheetDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { sheetDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override @Override
public void onDismiss(DialogInterface dialog) { public void onDismiss(DialogInterface dialog) {
mController.onDismiss(); mController.onDismiss(DialogDismissalCause.UNKNOWN);
} }
}); });
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.password_manager; package org.chromium.chrome.browser.password_manager;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
/** Class responsible for binding the model and the view. On bind, it lazily initializes the view /** Class responsible for binding the model and the view. On bind, it lazily initializes the view
...@@ -33,12 +34,7 @@ public class PasswordGenerationDialogViewBinder { ...@@ -33,12 +34,7 @@ public class PasswordGenerationDialogViewBinder {
} }
@Override @Override
public void onCancel() { public void onDismiss(@DialogDismissalCause int dismissalCause) {
mPasswordActionCallback.onResult(false);
}
@Override
public void onDismiss() {
mPasswordActionCallback.onResult(false); mPasswordActionCallback.onResult(false);
} }
} }
......
...@@ -12,6 +12,7 @@ import org.chromium.base.VisibleForTesting; ...@@ -12,6 +12,7 @@ import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.vr.VrModuleProvider; import org.chromium.chrome.browser.vr.VrModuleProvider;
...@@ -297,10 +298,7 @@ public class PermissionDialogController ...@@ -297,10 +298,7 @@ public class PermissionDialogController
} }
@Override @Override
public void onCancel() {} public void onDismiss(@DialogDismissalCause int dismissalCause) {
@Override
public void onDismiss() {
mDismissListener.onDismiss(null); mDismissListener.onDismiss(null);
mAppModalDialogView = null; mAppModalDialogView = null;
} }
......
...@@ -9,6 +9,7 @@ import android.content.Context; ...@@ -9,6 +9,7 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.view.View; import android.view.View;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogView; import org.chromium.chrome.browser.modaldialog.ModalDialogView;
...@@ -98,14 +99,11 @@ public class VrAlertDialog extends AlertDialog { ...@@ -98,14 +99,11 @@ public class VrAlertDialog extends AlertDialog {
*/ */
@Override @Override
public void dismiss() { public void dismiss() {
mModalDialogManager.cancelDialog(mModalDialogView); mModalDialogManager.dismissDialog(mModalDialogView);
} }
private ModalDialogView createView() { private ModalDialogView createView() {
ModalDialogView.Controller controller = new ModalDialogView.Controller() { ModalDialogView.Controller controller = new ModalDialogView.Controller() {
@Override
public void onCancel() {}
@Override @Override
public void onClick(int buttonType) { public void onClick(int buttonType) {
if (buttonType == ModalDialogView.ButtonType.POSITIVE) { if (buttonType == ModalDialogView.ButtonType.POSITIVE) {
...@@ -117,7 +115,7 @@ public class VrAlertDialog extends AlertDialog { ...@@ -117,7 +115,7 @@ public class VrAlertDialog extends AlertDialog {
} }
@Override @Override
public void onDismiss() {} public void onDismiss(@DialogDismissalCause int dialogDismissal) {}
}; };
final ModalDialogView.Params params = new ModalDialogView.Params(); final ModalDialogView.Params params = new ModalDialogView.Params();
......
...@@ -9,6 +9,7 @@ import android.view.View; ...@@ -9,6 +9,7 @@ import android.view.View;
import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
/** The presenter that shows a {@link ModalDialogView} in an Android dialog. */ /** The presenter that shows a {@link ModalDialogView} in an Android dialog. */
...@@ -42,6 +43,6 @@ public class VrModalPresenter extends ModalDialogManager.Presenter { ...@@ -42,6 +43,6 @@ public class VrModalPresenter extends ModalDialogManager.Presenter {
} }
public void closeCurrentDialog() { public void closeCurrentDialog() {
cancelCurrentDialog(); dismissCurrentDialog(DialogDismissalCause.UNKNOWN);
} }
} }
...@@ -36,6 +36,7 @@ import org.chromium.chrome.browser.ChromeActivity; ...@@ -36,6 +36,7 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.compositor.CompositorView; import org.chromium.chrome.browser.compositor.CompositorView;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager; import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.page_info.PageInfoController; import org.chromium.chrome.browser.page_info.PageInfoController;
import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.EmptyTabObserver;
...@@ -329,7 +330,7 @@ public class VrShell extends GvrLayout ...@@ -329,7 +330,7 @@ public class VrShell extends GvrLayout
private void injectVrHostedUiView() { private void injectVrHostedUiView() {
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.VR_BROWSING_NATIVE_ANDROID_UI)) return; if (!ChromeFeatureList.isEnabled(ChromeFeatureList.VR_BROWSING_NATIVE_ANDROID_UI)) return;
mNonVrModalDialogManager = mActivity.getModalDialogManager(); mNonVrModalDialogManager = mActivity.getModalDialogManager();
mNonVrModalDialogManager.cancelAllDialogs(); mNonVrModalDialogManager.dismissAllDialogs(DialogDismissalCause.UNKNOWN);
mVrModalPresenter = new VrModalPresenter(this); mVrModalPresenter = new VrModalPresenter(this);
mVrModalDialogManager = mVrModalDialogManager =
new ModalDialogManager(mVrModalPresenter, ModalDialogManager.ModalDialogType.APP); new ModalDialogManager(mVrModalPresenter, ModalDialogManager.ModalDialogType.APP);
...@@ -754,7 +755,7 @@ public class VrShell extends GvrLayout ...@@ -754,7 +755,7 @@ public class VrShell extends GvrLayout
public void shutdown() { public void shutdown() {
if (mVrBrowsingEnabled) { if (mVrBrowsingEnabled) {
if (mVrModalDialogManager != null) { if (mVrModalDialogManager != null) {
mVrModalDialogManager.cancelAllDialogs(); mVrModalDialogManager.dismissAllDialogs(DialogDismissalCause.UNKNOWN);
mActivity.setModalDialogManager(mNonVrModalDialogManager); mActivity.setModalDialogManager(mNonVrModalDialogManager);
mVrModalDialogManager = null; mVrModalDialogManager = null;
} }
......
...@@ -848,6 +848,7 @@ chrome_java_sources = [ ...@@ -848,6 +848,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/metrics/WebApkUma.java", "java/src/org/chromium/chrome/browser/metrics/WebApkUma.java",
"java/src/org/chromium/chrome/browser/metrics/WebappUma.java", "java/src/org/chromium/chrome/browser/metrics/WebappUma.java",
"java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java", "java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java",
"java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java", "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java", "java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java",
"java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java", "java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java",
......
...@@ -54,11 +54,16 @@ import java.util.List; ...@@ -54,11 +54,16 @@ import java.util.List;
public class ModalDialogManagerTest { public class ModalDialogManagerTest {
private static class TestObserver implements UrlFocusChangeListener { private static class TestObserver implements UrlFocusChangeListener {
public final CallbackHelper onUrlFocusChangedCallback = new CallbackHelper(); public final CallbackHelper onUrlFocusChangedCallback = new CallbackHelper();
public final CallbackHelper onDialogDismissedCallback = new CallbackHelper();
@Override @Override
public void onUrlFocusChange(boolean hasFocus) { public void onUrlFocusChange(boolean hasFocus) {
onUrlFocusChangedCallback.notifyCalled(); onUrlFocusChangedCallback.notifyCalled();
} }
public void onDialogDismissed() {
onDialogDismissedCallback.notifyCalled();
}
} }
@Rule @Rule
...@@ -69,6 +74,7 @@ public class ModalDialogManagerTest { ...@@ -69,6 +74,7 @@ public class ModalDialogManagerTest {
private ModalDialogManager mManager; private ModalDialogManager mManager;
private ModalDialogView[] mModalDialogViews; private ModalDialogView[] mModalDialogViews;
private TestObserver mTestObserver; private TestObserver mTestObserver;
private Integer mExpectedDismissalCause;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
...@@ -680,14 +686,67 @@ public class ModalDialogManagerTest { ...@@ -680,14 +686,67 @@ public class ModalDialogManagerTest {
checkCurrentPresenter(null); checkCurrentPresenter(null);
} }
@Test
@SmallTest
public void testDismiss_DismissalCause_BackPressed() throws Exception {
mExpectedDismissalCause = DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE;
int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
showDialog(0, ModalDialogType.APP);
showDialog(1, ModalDialogType.TAB);
// Dismiss the app modal dialog and veirify dimissal cause.
Espresso.pressBack();
mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
// Dismiss the tab modal dialog and veirify dimissal cause.
callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
Espresso.pressBack();
mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
mExpectedDismissalCause = null;
}
@Test
@SmallTest
public void testDismiss_DismissalCause_TabSwitched() throws Exception {
mExpectedDismissalCause = DialogDismissalCause.TAB_SWITCHED;
int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
// Open a new tab and make sure that the current tab is at index 0.
mActivityTestRule.loadUrlInNewTab("about:blank");
ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 0);
// Show a tab modal dialog and then switch tab.
showDialog(0, ModalDialogType.TAB);
ChromeTabUtils.switchTabInCurrentTabModel(mActivity, 1);
mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
mExpectedDismissalCause = null;
}
@Test
@SmallTest
public void testDismiss_DismissalCause_TabDestroyed() throws Exception {
mExpectedDismissalCause = DialogDismissalCause.TAB_DESTROYED;
int callCount = mTestObserver.onDialogDismissedCallback.getCallCount();
// Show a tab modal dialog and then close tab.
showDialog(0, ModalDialogType.TAB);
ChromeTabUtils.closeCurrentTab(InstrumentationRegistry.getInstrumentation(), mActivity);
mTestObserver.onDialogDismissedCallback.waitForCallback(callCount);
mExpectedDismissalCause = null;
}
private ModalDialogView createDialog(final int index) throws Exception { private ModalDialogView createDialog(final int index) throws Exception {
return ThreadUtils.runOnUiThreadBlocking(() -> { return ThreadUtils.runOnUiThreadBlocking(() -> {
ModalDialogView.Controller controller = new ModalDialogView.Controller() { ModalDialogView.Controller controller = new ModalDialogView.Controller() {
@Override @Override
public void onCancel() {} public void onDismiss(@DialogDismissalCause int dismissalCause) {
mTestObserver.onDialogDismissed();
@Override checkDialogDismissalCause(dismissalCause);
public void onDismiss() {} }
@Override @Override
public void onClick(int buttonType) { public void onClick(int buttonType) {
...@@ -755,4 +814,9 @@ public class ModalDialogManagerTest { ...@@ -755,4 +814,9 @@ public class ModalDialogManagerTest {
onView(withId(R.id.menu_button)).check(matches(isEnabled())); onView(withId(R.id.menu_button)).check(matches(isEnabled()));
} }
} }
private void checkDialogDismissalCause(int dismissalCause) {
if (mExpectedDismissalCause == null) return;
Assert.assertEquals(mExpectedDismissalCause.intValue(), dismissalCause);
}
} }
...@@ -39,10 +39,14 @@ base::WeakPtr<JavaScriptDialogAndroid> JavaScriptDialogAndroid::Create( ...@@ -39,10 +39,14 @@ base::WeakPtr<JavaScriptDialogAndroid> JavaScriptDialogAndroid::Create(
content::JavaScriptDialogType dialog_type, content::JavaScriptDialogType dialog_type,
const base::string16& message_text, const base::string16& message_text,
const base::string16& default_prompt_text, const base::string16& default_prompt_text,
content::JavaScriptDialogManager::DialogClosedCallback dialog_callback) { content::JavaScriptDialogManager::DialogClosedCallback
return (new JavaScriptDialogAndroid( callback_on_button_clicked,
parent_web_contents, alerting_web_contents, title, dialog_type, base::OnceClosure callback_on_cancelled) {
message_text, default_prompt_text, std::move(dialog_callback))) return (new JavaScriptDialogAndroid(parent_web_contents,
alerting_web_contents, title, dialog_type,
message_text, default_prompt_text,
std::move(callback_on_button_clicked),
std::move(callback_on_cancelled)))
->weak_factory_.GetWeakPtr(); ->weak_factory_.GetWeakPtr();
} }
...@@ -60,20 +64,23 @@ base::string16 JavaScriptDialogAndroid::GetUserInput() { ...@@ -60,20 +64,23 @@ base::string16 JavaScriptDialogAndroid::GetUserInput() {
void JavaScriptDialogAndroid::Accept(JNIEnv* env, void JavaScriptDialogAndroid::Accept(JNIEnv* env,
const JavaParamRef<jobject>&, const JavaParamRef<jobject>&,
const JavaParamRef<jstring>& prompt) { const JavaParamRef<jstring>& prompt) {
if (dialog_callback_) { if (callback_on_button_clicked_) {
base::string16 prompt_text = base::string16 prompt_text =
base::android::ConvertJavaStringToUTF16(env, prompt); base::android::ConvertJavaStringToUTF16(env, prompt);
std::move(dialog_callback_).Run(true, prompt_text); std::move(callback_on_button_clicked_).Run(true, prompt_text);
std::move(dialog_callback_).Reset();
} }
delete this; delete this;
} }
void JavaScriptDialogAndroid::Cancel(JNIEnv* env, void JavaScriptDialogAndroid::Cancel(JNIEnv* env,
const JavaParamRef<jobject>&) { const JavaParamRef<jobject>&,
if (dialog_callback_) { jboolean button_clicked) {
std::move(dialog_callback_).Run(false, base::string16()); if (button_clicked) {
std::move(dialog_callback_).Reset(); if (callback_on_button_clicked_) {
std::move(callback_on_button_clicked_).Run(false, base::string16());
}
} else if (callback_on_cancelled_) {
std::move(callback_on_cancelled_).Run();
} }
delete this; delete this;
} }
...@@ -85,8 +92,12 @@ JavaScriptDialogAndroid::JavaScriptDialogAndroid( ...@@ -85,8 +92,12 @@ JavaScriptDialogAndroid::JavaScriptDialogAndroid(
content::JavaScriptDialogType dialog_type, content::JavaScriptDialogType dialog_type,
const base::string16& message_text, const base::string16& message_text,
const base::string16& default_prompt_text, const base::string16& default_prompt_text,
content::JavaScriptDialogManager::DialogClosedCallback dialog_callback) content::JavaScriptDialogManager::DialogClosedCallback
: dialog_callback_(std::move(dialog_callback)), weak_factory_(this) { callback_on_button_clicked,
base::OnceClosure callback_on_cancelled)
: callback_on_button_clicked_(std::move(callback_on_button_clicked)),
callback_on_cancelled_(std::move(callback_on_cancelled)),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
......
...@@ -23,6 +23,10 @@ class JavaScriptDialogAndroid : public JavaScriptDialog { ...@@ -23,6 +23,10 @@ class JavaScriptDialogAndroid : public JavaScriptDialog {
public: public:
~JavaScriptDialogAndroid() override; ~JavaScriptDialogAndroid() override;
// Note on the two callbacks: |dialog_callback_on_button_clicked| is for the
// case where user responds to the dialog. |dialog_callback_on_cancelled| is
// for the case where user cancels the dialog without interacting with the
// dialog (e.g. clicks the navigate back button on Android).
static base::WeakPtr<JavaScriptDialogAndroid> Create( static base::WeakPtr<JavaScriptDialogAndroid> Create(
content::WebContents* parent_web_contents, content::WebContents* parent_web_contents,
content::WebContents* alerting_web_contents, content::WebContents* alerting_web_contents,
...@@ -30,7 +34,9 @@ class JavaScriptDialogAndroid : public JavaScriptDialog { ...@@ -30,7 +34,9 @@ class JavaScriptDialogAndroid : public JavaScriptDialog {
content::JavaScriptDialogType dialog_type, content::JavaScriptDialogType dialog_type,
const base::string16& message_text, const base::string16& message_text,
const base::string16& default_prompt_text, const base::string16& default_prompt_text,
content::JavaScriptDialogManager::DialogClosedCallback dialog_callback); content::JavaScriptDialogManager::DialogClosedCallback
callback_on_button_clicked,
base::OnceClosure callback_on_cancelled);
// JavaScriptDialog: // JavaScriptDialog:
void CloseDialogWithoutCallback() override; void CloseDialogWithoutCallback() override;
...@@ -39,23 +45,28 @@ class JavaScriptDialogAndroid : public JavaScriptDialog { ...@@ -39,23 +45,28 @@ class JavaScriptDialogAndroid : public JavaScriptDialog {
void Accept(JNIEnv* env, void Accept(JNIEnv* env,
const base::android::JavaParamRef<jobject>&, const base::android::JavaParamRef<jobject>&,
const base::android::JavaParamRef<jstring>& prompt); const base::android::JavaParamRef<jstring>& prompt);
void Cancel(JNIEnv* env, const base::android::JavaParamRef<jobject>&); void Cancel(JNIEnv* env,
const base::android::JavaParamRef<jobject>&,
jboolean button_clicked);
private: private:
JavaScriptDialogAndroid( JavaScriptDialogAndroid(content::WebContents* parent_web_contents,
content::WebContents* parent_web_contents, content::WebContents* alerting_web_contents,
content::WebContents* alerting_web_contents, const base::string16& title,
const base::string16& title, content::JavaScriptDialogType dialog_type,
content::JavaScriptDialogType dialog_type, const base::string16& message_text,
const base::string16& message_text, const base::string16& default_prompt_text,
const base::string16& default_prompt_text, content::JavaScriptDialogManager::DialogClosedCallback
content::JavaScriptDialogManager::DialogClosedCallback dialog_callback); callback_on_button_clicked,
base::OnceClosure callback_on_cancelled);
std::unique_ptr<JavaScriptDialogAndroid> dialog_; std::unique_ptr<JavaScriptDialogAndroid> dialog_;
ScopedJavaGlobalRef<jobject> dialog_jobject_; ScopedJavaGlobalRef<jobject> dialog_jobject_;
JavaObjectWeakGlobalRef jwindow_weak_ref_; JavaObjectWeakGlobalRef jwindow_weak_ref_;
content::JavaScriptDialogManager::DialogClosedCallback dialog_callback_; content::JavaScriptDialogManager::DialogClosedCallback
callback_on_button_clicked_;
base::OnceClosure callback_on_cancelled_;
base::WeakPtrFactory<JavaScriptDialogAndroid> weak_factory_; base::WeakPtrFactory<JavaScriptDialogAndroid> weak_factory_;
......
...@@ -150,7 +150,8 @@ base::WeakPtr<JavaScriptDialog> CreateNewDialog( ...@@ -150,7 +150,8 @@ base::WeakPtr<JavaScriptDialog> CreateNewDialog(
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
return JavaScriptDialogAndroid::Create( return JavaScriptDialogAndroid::Create(
parent_web_contents, alerting_web_contents, title, dialog_type, parent_web_contents, alerting_web_contents, title, dialog_type,
message_text, default_prompt_text, std::move(dialog_callback)); message_text, default_prompt_text, std::move(dialog_callback),
std::move(dialog_closed_callback));
#else #else
return JavaScriptDialogViews::Create( return JavaScriptDialogViews::Create(
parent_web_contents, alerting_web_contents, title, dialog_type, parent_web_contents, alerting_web_contents, title, dialog_type,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment