Reland "WebLayer: add form repost confirmation dialog."
This relands commit ce01fae55d
but instead
of including dialog_strings.grdp from two different grd targets, it uses
the WebLayer components string whitelist. A small amount of extra work
had to be done to package the strings tagged
formatter_data="android_java" in components_strings.grd into a java
string target.
TBR=tedchoc@chromium.org
Bug: 1058495
Change-Id: I94aae6e9f75f5252123fb83b6ade28cbf5c1f3ba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2124699
Reviewed-by: Evan Stade <estade@chromium.org>
Reviewed-by: Clark DuVall <cduvall@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Commit-Queue: Evan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#754138}
This commit is contained in:
chrome/android/java/src/org/chromium/chrome/browser/tab_activity_glue
components
ui/android
weblayer
BUILD.gn
browser
grit_strings_whitelist.txttest
data
@ -46,6 +46,7 @@ import org.chromium.content_public.browser.WebContents;
|
||||
import org.chromium.ui.modaldialog.DialogDismissalCause;
|
||||
import org.chromium.ui.modaldialog.ModalDialogManager;
|
||||
import org.chromium.ui.modaldialog.ModalDialogProperties;
|
||||
import org.chromium.ui.modaldialog.SimpleModalDialogController;
|
||||
import org.chromium.ui.modelutil.PropertyModel;
|
||||
import org.chromium.ui.mojom.WindowOpenDisposition;
|
||||
|
||||
@ -374,36 +375,22 @@ public class ActivityTabWebContentsDelegateAndroid extends TabWebContentsDelegat
|
||||
}
|
||||
|
||||
ModalDialogManager modalDialogManager = mActivity.getModalDialogManager();
|
||||
|
||||
ModalDialogProperties.Controller dialogController = new ModalDialogProperties.Controller() {
|
||||
@Override
|
||||
public void onClick(PropertyModel model, int buttonType) {
|
||||
if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
|
||||
modalDialogManager.dismissDialog(
|
||||
model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
|
||||
} else if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) {
|
||||
modalDialogManager.dismissDialog(
|
||||
model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(PropertyModel model, int dismissalCause) {
|
||||
if (!mTab.isInitialized()) return;
|
||||
switch (dismissalCause) {
|
||||
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
|
||||
mTab.getWebContents().getNavigationController().continuePendingReload();
|
||||
break;
|
||||
case DialogDismissalCause.ACTIVITY_DESTROYED:
|
||||
case DialogDismissalCause.TAB_DESTROYED:
|
||||
// Intentionally ignored as the tab object is gone.
|
||||
break;
|
||||
default:
|
||||
mTab.getWebContents().getNavigationController().cancelPendingReload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
ModalDialogProperties.Controller dialogController =
|
||||
new SimpleModalDialogController(modalDialogManager, (Integer dismissalCause) -> {
|
||||
if (!mTab.isInitialized()) return;
|
||||
switch (dismissalCause) {
|
||||
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
|
||||
mTab.getWebContents().getNavigationController().continuePendingReload();
|
||||
break;
|
||||
case DialogDismissalCause.ACTIVITY_DESTROYED:
|
||||
case DialogDismissalCause.TAB_DESTROYED:
|
||||
// Intentionally ignored as the tab object is gone.
|
||||
break;
|
||||
default:
|
||||
mTab.getWebContents().getNavigationController().cancelPendingReload();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
Resources resources = mActivity.getResources();
|
||||
PropertyModel dialogModel =
|
||||
|
@ -179,7 +179,7 @@
|
||||
<message name="IDS_INFOBAR_MISSING_LOCATION_PERMISSION_TEXT" desc="Text shown in an infobar when a website has requested access to the location capabilities, but Chrome is missing the Android location permission.">
|
||||
Chrome needs access to your location to share your location with this site.
|
||||
</message>
|
||||
<message name="IDS_INFOBAR_UPDATE_PERMISSIONS_BUTTON_TEXT" desc="Button text shown when Chrome does not have the necessary permission required to complete the requested tasks (e.g. a website has request location information, but Chrome is missing that Android permission)." formatter_data="android_java">
|
||||
<message name="IDS_INFOBAR_UPDATE_PERMISSIONS_BUTTON_TEXT" desc="Button text shown when Chrome does not have the necessary permission required to complete the requested tasks (e.g. a website has request location information, but Chrome is missing that Android permission).">
|
||||
Continue
|
||||
</message>
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
<message name="IDS_STORAGE_ACCESS_INFOBAR_TEXT" desc="Permission request shown if the user is visiting a site needs access to its data while it is embedded into another site.">
|
||||
<ph name="EMBEDDED_URL">$1<ex>news.site</ex></ph> wants to use cookies and site data on <ph name="TOP_LEVEL_URL">$2<ex>content_domain.site</ex></ph>
|
||||
</message>
|
||||
<message name="IDS_INFOBAR_UPDATE_PERMISSIONS_BUTTON_TEXT" desc="Button text shown when Chrome does not have the necessary permission required to complete the requested tasks (e.g. a website has request location information, but Chrome is missing that Android permission)." formatter_data="android_java">
|
||||
<message name="IDS_INFOBAR_UPDATE_PERMISSIONS_BUTTON_TEXT" desc="Button text shown when Chrome does not have the necessary permission required to complete the requested tasks (e.g. a website has request location information, but Chrome is missing that Android permission).">
|
||||
Continue
|
||||
</message>
|
||||
<message name="IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_INFOBAR_TEXT" desc="Text requesting permission for a site to access the computer's microphone and camera.">
|
||||
|
@ -322,6 +322,7 @@ android_library("ui_full_java") {
|
||||
"java/src/org/chromium/ui/modaldialog/ModalDialogManager.java",
|
||||
"java/src/org/chromium/ui/modaldialog/ModalDialogManagerHolder.java",
|
||||
"java/src/org/chromium/ui/modaldialog/ModalDialogProperties.java",
|
||||
"java/src/org/chromium/ui/modaldialog/SimpleModalDialogController.java",
|
||||
"java/src/org/chromium/ui/modelutil/ForwardingListObservable.java",
|
||||
"java/src/org/chromium/ui/modelutil/LayoutViewBuilder.java",
|
||||
"java/src/org/chromium/ui/modelutil/LazyConstructionPropertyMcp.java",
|
||||
|
@ -0,0 +1,46 @@
|
||||
// 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.ui.modaldialog;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.chromium.base.Callback;
|
||||
import org.chromium.ui.modelutil.PropertyModel;
|
||||
|
||||
/**
|
||||
* A default implementation of Controller which dismisses the dialog when a button is clicked.
|
||||
*
|
||||
* The result of the dialog is passed back via a Callback.
|
||||
*/
|
||||
public class SimpleModalDialogController implements ModalDialogProperties.Controller {
|
||||
private final ModalDialogManager mModalDialogManager;
|
||||
private Callback<Integer> mActionCallback;
|
||||
|
||||
/**
|
||||
* @param modalDialogManager the dialog manager where the dialog will be shown.
|
||||
* @param action a callback which will be run with the result of the confirmation.
|
||||
*/
|
||||
public SimpleModalDialogController(
|
||||
ModalDialogManager modalDialogManager, @NonNull Callback<Integer> action) {
|
||||
mModalDialogManager = modalDialogManager;
|
||||
mActionCallback = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(PropertyModel model, int buttonType) {
|
||||
if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
|
||||
mModalDialogManager.dismissDialog(model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
|
||||
} else {
|
||||
mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(PropertyModel model, int dismissalCause) {
|
||||
Callback<Integer> action = mActionCallback;
|
||||
mActionCallback = null;
|
||||
action.onResult(dismissalCause);
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import("//tools/grit/repack.gni")
|
||||
import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
|
||||
if (is_android) {
|
||||
import("//build/config/android/config.gni")
|
||||
import("//build/config/android/rules.gni")
|
||||
} else if (is_mac) {
|
||||
import("//build/config/mac/rules.gni")
|
||||
import("//build/mac/tweak_info_plist.gni")
|
||||
@ -33,6 +34,12 @@ source_set("android_descriptors") {
|
||||
}
|
||||
|
||||
if (is_android) {
|
||||
weblayer_components_strings_java_resources =
|
||||
[ "java/res/values/components_strings.xml" ] +
|
||||
process_file_template(
|
||||
android_bundle_locales_as_resources,
|
||||
[ "java/res/values-{{source_name_part}}/components_strings.xml" ])
|
||||
|
||||
grit("generate_components_strings") {
|
||||
source = "../components/components_strings.grd"
|
||||
|
||||
@ -46,13 +53,8 @@ if (is_android) {
|
||||
whitelist,
|
||||
]
|
||||
outputs =
|
||||
[
|
||||
"grit/components_strings.h",
|
||||
"java/res/values/components_strings.xml",
|
||||
] +
|
||||
process_file_template(
|
||||
android_bundle_locales_as_resources,
|
||||
[ "java/res/values-{{source_name_part}}/components_strings.xml" ]) +
|
||||
weblayer_components_strings_java_resources +
|
||||
[ "grit/components_strings.h" ] +
|
||||
process_file_template(locales_with_fake_bidi,
|
||||
[ "components_strings_{{source_name_part}}.pak" ])
|
||||
}
|
||||
@ -84,6 +86,13 @@ if (is_android) {
|
||||
treat_as_locale_paks = true
|
||||
deps = [ ":weblayer_locales" ]
|
||||
}
|
||||
|
||||
java_strings_grd_prebuilt("components_java_strings") {
|
||||
grit_output_dir = "$root_gen_dir/weblayer/java/res"
|
||||
generated_files =
|
||||
rebase_path(weblayer_components_strings_java_resources, "java/res", ".")
|
||||
deps = [ ":generate_components_strings" ]
|
||||
}
|
||||
}
|
||||
|
||||
source_set("weblayer_lib_base") {
|
||||
|
@ -7,6 +7,7 @@ package org.chromium.weblayer.test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
|
||||
@ -27,6 +28,7 @@ import org.chromium.weblayer.NavigationCallback;
|
||||
import org.chromium.weblayer.NavigationController;
|
||||
import org.chromium.weblayer.NavigationState;
|
||||
import org.chromium.weblayer.Tab;
|
||||
import org.chromium.weblayer.TabCallback;
|
||||
import org.chromium.weblayer.shell.InstrumentationActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -442,6 +444,41 @@ public class NavigationTest {
|
||||
assertEquals(mCallback.onCompletedCallback.getNavigationState(), NavigationState.COMPLETE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
public void testRepostConfirmation() throws Exception {
|
||||
// Load a page with a form.
|
||||
InstrumentationActivity activity =
|
||||
mActivityTestRule.launchShellWithUrl(mActivityTestRule.getTestDataURL("form.html"));
|
||||
assertNotNull(activity);
|
||||
setNavigationCallback(activity);
|
||||
|
||||
// Touch the page; this should submit the form.
|
||||
int currentCallCount = mCallback.onCompletedCallback.getCallCount();
|
||||
EventUtils.simulateTouchCenterOfView(activity.getWindow().getDecorView());
|
||||
String targetUrl = mActivityTestRule.getTestDataURL("simple_page.html");
|
||||
mCallback.onCompletedCallback.assertCalledWith(currentCallCount, targetUrl);
|
||||
|
||||
// Make sure a tab modal shows after we attempt a reload.
|
||||
Boolean isTabModalShowingResult[] = new Boolean[1];
|
||||
CallbackHelper callbackHelper = new CallbackHelper();
|
||||
runOnUiThreadBlocking(() -> {
|
||||
Tab tab = activity.getTab();
|
||||
TabCallback callback = new TabCallback() {
|
||||
@Override
|
||||
public void onTabModalStateChanged(boolean isTabModalShowing) {
|
||||
isTabModalShowingResult[0] = isTabModalShowing;
|
||||
callbackHelper.notifyCalled();
|
||||
}
|
||||
};
|
||||
tab.registerTabCallback(callback);
|
||||
tab.getNavigationController().reload();
|
||||
});
|
||||
|
||||
callbackHelper.waitForFirst();
|
||||
assertTrue(isTabModalShowingResult[0]);
|
||||
}
|
||||
|
||||
private void setNavigationCallback(InstrumentationActivity activity) {
|
||||
runOnUiThreadBlocking(
|
||||
()
|
||||
|
@ -9,7 +9,11 @@ import("//weblayer/variables.gni")
|
||||
android_resources("weblayer_resources") {
|
||||
sources = [ "res/layout/weblayer_url_bar.xml" ]
|
||||
custom_package = "org.chromium.weblayer_private"
|
||||
deps = [ "//components/permissions/android:java_resources" ]
|
||||
deps = [
|
||||
"//components/browser_ui/strings/android:browser_ui_strings_grd",
|
||||
"//components/permissions/android:java_resources",
|
||||
"//weblayer:components_java_strings",
|
||||
]
|
||||
}
|
||||
|
||||
generate_product_config_srcjar("weblayer_product_config") {
|
||||
|
@ -5,6 +5,7 @@
|
||||
package org.chromium.weblayer_private;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.RemoteException;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.view.View;
|
||||
@ -20,6 +21,8 @@ import org.chromium.content_public.browser.WebContents;
|
||||
import org.chromium.ui.modaldialog.DialogDismissalCause;
|
||||
import org.chromium.ui.modaldialog.ModalDialogManager;
|
||||
import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
|
||||
import org.chromium.ui.modaldialog.ModalDialogProperties;
|
||||
import org.chromium.ui.modaldialog.SimpleModalDialogController;
|
||||
import org.chromium.ui.modelutil.PropertyModel;
|
||||
|
||||
/**
|
||||
@ -223,4 +226,39 @@ public final class BrowserViewController
|
||||
return mModalDialogManager.dismissActiveDialogOfType(
|
||||
ModalDialogType.TAB, DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user to confirm a page reload on a POSTed page.
|
||||
*/
|
||||
public void showRepostFormWarningDialog() {
|
||||
ModalDialogProperties.Controller dialogController =
|
||||
new SimpleModalDialogController(mModalDialogManager, (Integer dismissalCause) -> {
|
||||
WebContents webContents = mTab == null ? null : mTab.getWebContents();
|
||||
if (webContents == null) return;
|
||||
switch (dismissalCause) {
|
||||
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
|
||||
webContents.getNavigationController().continuePendingReload();
|
||||
break;
|
||||
default:
|
||||
webContents.getNavigationController().cancelPendingReload();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
Resources resources = mWindowAndroid.getContext().get().getResources();
|
||||
PropertyModel dialogModel =
|
||||
new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
|
||||
.with(ModalDialogProperties.CONTROLLER, dialogController)
|
||||
.with(ModalDialogProperties.TITLE, resources,
|
||||
R.string.http_post_warning_title)
|
||||
.with(ModalDialogProperties.MESSAGE, resources, R.string.http_post_warning)
|
||||
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
|
||||
R.string.http_post_warning_resend)
|
||||
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
|
||||
R.string.cancel)
|
||||
.with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
|
||||
.build();
|
||||
|
||||
mModalDialogManager.showDialog(dialogModel, ModalDialogManager.ModalDialogType.TAB, true);
|
||||
}
|
||||
}
|
||||
|
@ -560,6 +560,16 @@ public final class TabImpl extends ITab.Stub {
|
||||
mBrowserControlsDelegates.get(reason).set(constraint);
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
public void showRepostFormWarningDialog() {
|
||||
BrowserViewController viewController = getViewController();
|
||||
if (viewController == null) {
|
||||
mWebContents.getNavigationController().cancelPendingReload();
|
||||
} else {
|
||||
viewController.showRepostFormWarningDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private static String nonEmptyOrNull(String s) {
|
||||
return TextUtils.isEmpty(s) ? null : s;
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ void NavigationControllerImpl::GoToIndex(int index) {
|
||||
}
|
||||
|
||||
void NavigationControllerImpl::Reload() {
|
||||
web_contents()->GetController().Reload(content::ReloadType::NORMAL, false);
|
||||
web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
|
||||
}
|
||||
|
||||
void NavigationControllerImpl::Stop() {
|
||||
|
@ -449,6 +449,15 @@ content::WebContents* TabImpl::OpenURLFromTab(
|
||||
return source;
|
||||
}
|
||||
|
||||
void TabImpl::ShowRepostFormWarningDialog(content::WebContents* source) {
|
||||
#if defined(OS_ANDROID)
|
||||
Java_TabImpl_showRepostFormWarningDialog(base::android::AttachCurrentThread(),
|
||||
java_impl_);
|
||||
#else
|
||||
source->GetController().CancelPendingReload();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TabImpl::NavigationStateChanged(content::WebContents* source,
|
||||
content::InvalidateTypes changed_flags) {
|
||||
DCHECK_EQ(web_contents_.get(), source);
|
||||
|
@ -152,6 +152,7 @@ class TabImpl : public Tab,
|
||||
content::WebContents* OpenURLFromTab(
|
||||
content::WebContents* source,
|
||||
const content::OpenURLParams& params) override;
|
||||
void ShowRepostFormWarningDialog(content::WebContents* source) override;
|
||||
void NavigationStateChanged(content::WebContents* source,
|
||||
content::InvalidateTypes changed_flags) override;
|
||||
content::JavaScriptDialogManager* GetJavaScriptDialogManager(
|
||||
|
@ -62,7 +62,9 @@ IDS_FLASH_PERMISSION_WARNING_FRAGMENT
|
||||
IDS_GEOLOCATION_INFOBAR_PERMISSION_FRAGMENT
|
||||
IDS_GEOLOCATION_INFOBAR_TEXT
|
||||
IDS_GEOLOCATION_INFOBAR_TEXT
|
||||
IDS_INFOBAR_UPDATE_PERMISSIONS_BUTTON_TEXT
|
||||
IDS_HTTP_POST_WARNING
|
||||
IDS_HTTP_POST_WARNING_RESEND
|
||||
IDS_HTTP_POST_WARNING_TITLE
|
||||
IDS_JAVASCRIPT_MESSAGEBOX_TITLE
|
||||
IDS_JAVASCRIPT_MESSAGEBOX_TITLE_IFRAME
|
||||
IDS_JAVASCRIPT_MESSAGEBOX_TITLE_NONSTANDARD_URL
|
||||
|
14
weblayer/test/data/form.html
Normal file
14
weblayer/test/data/form.html
Normal file
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
<form id="form1" action="simple_page.html" method="post">
|
||||
<input type="text" name="name" value="Name"><br>
|
||||
<input type="text" name="address" value="Address"><br>
|
||||
<input type="text" name="city" value="City"><br>
|
||||
</form>
|
||||
</body>
|
||||
<script>
|
||||
document.addEventListener('touchend', function(e) {
|
||||
document.forms['form1'].submit();
|
||||
}, false);
|
||||
</script>
|
||||
</html>
|
Reference in New Issue
Block a user