Introduce 15s timeout for navigation chains launching apps
Websites should not be given indefinitely long to redirect to an app, as we don't want an unattended page to launch an app. This change unifies the xhr-initiated and non-xhr-initiated navigation chains to timeout after 15 seconds from when the user initiated the navigation (or xhr). (Technically if the site waits until the user activation in blink is about to expire before kicking off the navigation then up to 20 seconds can be taken.) If the navigation chain is expired and no fallback URL is provided, a Message is shown to the user asking if they would like to launch the app. Future changes would ideally replace the UserGestureCarryover IPC with the time the user activation took place and set a bit on the NavigationHandle for navigations that happened during a resource request callback in javascript. However, there's no urgent need to make such changes. Doc: https://docs.google.com/document/d/1NVk_3eNRJ3aTXePTKk-9tC36LtNVvMUFoSeIc9cBi48/edit# Bug: 1311399 Change-Id: I40b13dc37195d1f9ccda9bf304a4c1483edbf695 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3746265 Reviewed-by: Colin Blundell <blundell@chromium.org> Reviewed-by: Bo Liu <boliu@chromium.org> Commit-Queue: Michael Thiessen <mthiesse@chromium.org> Reviewed-by: Yaron Friedman <yfriedman@chromium.org> Cr-Commit-Position: refs/heads/main@{#1023725}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
64a159eee7
commit
332dadb68e
android_webview
chrome
android
java
src
org
chromium
chrome
browser
javatests
src
org
chromium
chrome
browser
browser
test
data
android
url_overriding
components
external_intents
android
java
src
org
chromium
components
javatests
src
org
chromium
components
external_intents
navigation_interception
android
java
src
org
chromium
components
navigation_interception
content/public/android/java/src/org/chromium/content_public/browser
@ -296,7 +296,7 @@ void AwWebContentsDelegate::UpdateUserGestureCarryoverInfo(
|
||||
auto* intercept_navigation_delegate =
|
||||
navigation_interception::InterceptNavigationDelegate::Get(web_contents);
|
||||
if (intercept_navigation_delegate)
|
||||
intercept_navigation_delegate->UpdateLastUserGestureCarryoverTimestamp();
|
||||
intercept_navigation_delegate->OnResourceRequestWithGesture();
|
||||
}
|
||||
|
||||
scoped_refptr<content::FileSelectListener>
|
||||
|
@ -689,8 +689,7 @@ public class AwContents implements SmartClipProvider {
|
||||
//
|
||||
private class InterceptNavigationDelegateImpl extends InterceptNavigationDelegate {
|
||||
@Override
|
||||
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle, GURL escapedUrl,
|
||||
boolean applyUserGestureCarryover) {
|
||||
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle, GURL escapedUrl) {
|
||||
// The shouldOverrideUrlLoading call might have resulted in posting messages to the
|
||||
// UI thread. Using sendMessage here (instead of calling onPageStarted directly)
|
||||
// will allow those to run in order.
|
||||
|
@ -161,8 +161,7 @@ public class OverlayPanelContent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle, GURL escapedUrl,
|
||||
boolean applyUserGestureCarryover) {
|
||||
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle, GURL escapedUrl) {
|
||||
// If either of the required params for the delegate are null, do not call the
|
||||
// delegate and ignore the navigation.
|
||||
if (mExternalNavHandler == null || navigationHandle == null) return true;
|
||||
|
@ -228,8 +228,8 @@ public class ReaderModeManager extends EmptyTabObserver implements UserData {
|
||||
|
||||
mCustomTabNavigationDelegate = new InterceptNavigationDelegate() {
|
||||
@Override
|
||||
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle,
|
||||
GURL escapedUrl, boolean applyUserGestureCarryover) {
|
||||
public boolean shouldIgnoreNavigation(
|
||||
NavigationHandle navigationHandle, GURL escapedUrl) {
|
||||
if (DomDistillerUrlUtils.isDistilledPage(navigationHandle.getUrl())
|
||||
|| navigationHandle.isExternalProtocol()) {
|
||||
return false;
|
||||
|
@ -17,6 +17,7 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.lifecycle.Stage;
|
||||
import android.text.TextUtils;
|
||||
@ -35,6 +36,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.mockito.quality.Strictness;
|
||||
@ -133,8 +135,6 @@ public class UrlOverridingTest {
|
||||
BASE_PATH + "navigation_from_xhr_callback_parent_frame.html";
|
||||
private static final String NAVIGATION_FROM_XHR_CALLBACK_AND_SHORT_TIMEOUT_PAGE =
|
||||
BASE_PATH + "navigation_from_xhr_callback_and_short_timeout.html";
|
||||
private static final String NAVIGATION_FROM_XHR_CALLBACK_AND_LONG_TIMEOUT_PAGE =
|
||||
BASE_PATH + "navigation_from_xhr_callback_and_long_timeout.html";
|
||||
private static final String NAVIGATION_WITH_FALLBACK_URL_PAGE =
|
||||
BASE_PATH + "navigation_with_fallback_url.html";
|
||||
private static final String NAVIGATION_WITH_FALLBACK_URL_PARENT_FRAME_PAGE =
|
||||
@ -171,6 +171,9 @@ public class UrlOverridingTest {
|
||||
@Mock
|
||||
private RedirectHandler mRedirectHandler;
|
||||
|
||||
@Spy
|
||||
private RedirectHandler mSpyRedirectHandler;
|
||||
|
||||
private static class TestTabObserver extends EmptyTabObserver {
|
||||
private final CallbackHelper mFinishCallback;
|
||||
private final CallbackHelper mFailCallback;
|
||||
@ -579,11 +582,30 @@ public class UrlOverridingTest {
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
public void testNavigationFromXHRCallbackAndLongTimeout() {
|
||||
public void testNavigationFromXHRCallbackAndLongTimeout() throws Exception {
|
||||
mActivityTestRule.startMainActivityOnBlankPage();
|
||||
loadUrlAndWaitForIntentUrl(
|
||||
mTestServer.getURL(NAVIGATION_FROM_XHR_CALLBACK_AND_LONG_TIMEOUT_PAGE), true,
|
||||
|
||||
final Tab tab = mActivityTestRule.getActivity().getActivityTab();
|
||||
TestThreadUtils.runOnUiThreadBlocking(
|
||||
() -> RedirectHandlerTabHelper.swapHandlerFor(tab, mSpyRedirectHandler));
|
||||
|
||||
// This is a little fragile to code changes, but better than waiting 15 real seconds.
|
||||
Mockito.doReturn(SystemClock.elapsedRealtime()) // Initial Navigation create
|
||||
.doReturn(SystemClock.elapsedRealtime()) // Initial Navigation shouldOverride
|
||||
.doReturn(SystemClock.elapsedRealtime()) // XHR Navigation create
|
||||
.doReturn(SystemClock.elapsedRealtime()
|
||||
+ RedirectHandler.NAVIGATION_CHAIN_TIMEOUT_MILLIS + 1) // xhr callback
|
||||
.when(mSpyRedirectHandler)
|
||||
.currentRealtime();
|
||||
|
||||
@OverrideUrlLoadingResultType
|
||||
int result = loadUrlAndWaitForIntentUrl(
|
||||
mTestServer.getURL(NAVIGATION_FROM_XHR_CALLBACK_AND_SHORT_TIMEOUT_PAGE), true,
|
||||
false);
|
||||
|
||||
Assert.assertEquals(OverrideUrlLoadingResultType.OVERRIDE_WITH_ASYNC_ACTION, result);
|
||||
|
||||
assertMessagePresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -111,11 +111,10 @@ public class InterceptNavigationDelegateTest {
|
||||
new InterceptNavigationDelegateClientImpl(tab);
|
||||
InterceptNavigationDelegateImpl delegate = new InterceptNavigationDelegateImpl(client) {
|
||||
@Override
|
||||
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle,
|
||||
GURL escapedUrl, boolean applyUserGestureCarryover) {
|
||||
public boolean shouldIgnoreNavigation(
|
||||
NavigationHandle navigationHandle, GURL escapedUrl) {
|
||||
mNavParamHistory.add(navigationHandle);
|
||||
return super.shouldIgnoreNavigation(
|
||||
navigationHandle, escapedUrl, applyUserGestureCarryover);
|
||||
return super.shouldIgnoreNavigation(navigationHandle, escapedUrl);
|
||||
}
|
||||
};
|
||||
client.initializeWithDelegate(delegate);
|
||||
|
@ -439,7 +439,7 @@ void TabWebContentsDelegateAndroid::UpdateUserGestureCarryoverInfo(
|
||||
auto* intercept_navigation_delegate =
|
||||
navigation_interception::InterceptNavigationDelegate::Get(web_contents);
|
||||
if (intercept_navigation_delegate)
|
||||
intercept_navigation_delegate->UpdateLastUserGestureCarryoverTimestamp();
|
||||
intercept_navigation_delegate->OnResourceRequestWithGesture();
|
||||
}
|
||||
|
||||
content::PictureInPictureResult
|
||||
|
@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<script>
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
|
||||
function openApp() {
|
||||
window.location = 'intent://test/#Intent;scheme=externalappscheme;end';
|
||||
};
|
||||
|
||||
function xhrOnReadyStateChange() {
|
||||
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
|
||||
setTimeout(openApp, 11000);
|
||||
}
|
||||
};
|
||||
|
||||
function xhrAndOpenApp() {
|
||||
xmlhttp.onreadystatechange = xhrOnReadyStateChange;
|
||||
xmlhttp.open("GET", 'hello.html' , true);
|
||||
xmlhttp.send();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body style='height:10000px;' onclick='xhrAndOpenApp();'>
|
||||
Click page to open App!!
|
||||
</body>
|
||||
</html>
|
@ -930,6 +930,24 @@ public class ExternalNavigationHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* See RedirectHandler#NAVIGATION_CHAIN_TIMEOUT_MILLIS for details. We don't want an unattended
|
||||
* page to redirect to an app.
|
||||
*/
|
||||
private boolean isNavigationChainExpired(ExternalNavigationParams params) {
|
||||
if (params.getRedirectHandler() != null
|
||||
&& params.getRedirectHandler().isNavigationChainExpired()) {
|
||||
if (DEBUG) {
|
||||
Log.i(TAG,
|
||||
"Navigation chain expired "
|
||||
+ "(a page waited more than %d seconds to redirect).",
|
||||
RedirectHandler.NAVIGATION_CHAIN_TIMEOUT_MILLIS);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the intent can't be resolved, we should fall back to the browserFallbackUrl, or try to
|
||||
* find the app on the market if no fallback is provided.
|
||||
@ -1471,18 +1489,13 @@ public class ExternalNavigationHandler {
|
||||
QueryIntentActivitiesSupplier resolvingInfos =
|
||||
new QueryIntentActivitiesSupplier(targetIntent);
|
||||
|
||||
boolean requiresPromptForExternalIntent = false;
|
||||
|
||||
if (redirectShouldStayInApp(params, isExternalProtocol, targetIntent, resolvingInfos)) {
|
||||
requiresPromptForExternalIntent = true;
|
||||
}
|
||||
|
||||
boolean intentMatchesNonDefaultWebApk =
|
||||
intentMatchesNonDefaultWebApk(params, resolvingInfos);
|
||||
if (!preferToShowIntentPicker(params, isExternalProtocol, incomingIntentRedirect,
|
||||
intentMatchesNonDefaultWebApk)) {
|
||||
requiresPromptForExternalIntent = true;
|
||||
}
|
||||
|
||||
boolean requiresPromptForExternalIntent = isNavigationChainExpired(params)
|
||||
|| redirectShouldStayInApp(params, isExternalProtocol, targetIntent, resolvingInfos)
|
||||
|| !preferToShowIntentPicker(params, isExternalProtocol, incomingIntentRedirect,
|
||||
intentMatchesNonDefaultWebApk);
|
||||
|
||||
// Short-circuit expensive quertyIntentActivities calls below since we won't prompt anyways
|
||||
// for protocols the browser can handle.
|
||||
|
@ -24,6 +24,7 @@ import org.chromium.content_public.browser.NavigationHandle;
|
||||
import org.chromium.content_public.browser.UiThreadTaskTraits;
|
||||
import org.chromium.content_public.browser.WebContents;
|
||||
import org.chromium.content_public.common.ConsoleMessageLevel;
|
||||
import org.chromium.ui.base.PageTransition;
|
||||
import org.chromium.url.GURL;
|
||||
import org.chromium.url.Origin;
|
||||
|
||||
@ -103,8 +104,7 @@ public class InterceptNavigationDelegateImpl extends InterceptNavigationDelegate
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldIgnoreNavigation(
|
||||
NavigationHandle navigationHandle, GURL escapedUrl, boolean applyUserGestureCarryover) {
|
||||
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle, GURL escapedUrl) {
|
||||
mClient.onNavigationStarted(navigationHandle);
|
||||
|
||||
GURL url = escapedUrl;
|
||||
@ -135,12 +135,6 @@ public class InterceptNavigationDelegateImpl extends InterceptNavigationDelegate
|
||||
return false;
|
||||
}
|
||||
|
||||
// Temporarily apply User Gesture Carryover exception for resource requests to the
|
||||
// NavigationHandle.
|
||||
if (applyUserGestureCarryover) {
|
||||
assert !navigationHandle.hasUserGesture();
|
||||
navigationHandle.setUserGestureForCarryover(true);
|
||||
}
|
||||
redirectHandler.updateNewUrlLoading(navigationHandle.pageTransition(),
|
||||
navigationHandle.isRedirect(), navigationHandle.hasUserGesture(),
|
||||
lastUserInteractionTime, getLastCommittedEntryIndex(), isInitialNavigation());
|
||||
@ -155,10 +149,6 @@ public class InterceptNavigationDelegateImpl extends InterceptNavigationDelegate
|
||||
|
||||
mClient.onDecisionReachedForNavigation(navigationHandle, result);
|
||||
|
||||
if (applyUserGestureCarryover) {
|
||||
navigationHandle.setUserGestureForCarryover(false);
|
||||
}
|
||||
|
||||
boolean isExternalProtocol = !UrlUtilities.isAcceptedScheme(params.getUrl());
|
||||
String protocolType = isExternalProtocol ? "ExternalProtocol" : "InternalProtocol";
|
||||
RecordHistogram.recordEnumeratedHistogram(
|
||||
@ -186,6 +176,16 @@ public class InterceptNavigationDelegateImpl extends InterceptNavigationDelegate
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceRequestWithGesture() {
|
||||
// LINK is the default transition type, and is generally used for everything coming from a
|
||||
// renderer that isn't a form submission (or subframe).
|
||||
@PageTransition
|
||||
int transition = PageTransition.LINK;
|
||||
mClient.getOrCreateRedirectHandler().updateNewUrlLoading(transition, false, true,
|
||||
mClient.getLastUserInteractionTime(), getLastCommittedEntryIndex(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ExternalNavigationParams.Builder to generate ExternalNavigationParams for
|
||||
* ExternalNavigationHandler#shouldOverrideUrlLoading().
|
||||
|
@ -11,6 +11,8 @@ import android.os.SystemClock;
|
||||
import android.provider.Browser;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import org.chromium.base.ContextUtils;
|
||||
import org.chromium.base.Function;
|
||||
import org.chromium.base.IntentUtils;
|
||||
@ -39,6 +41,14 @@ public class RedirectHandler {
|
||||
private static final int NAVIGATION_TYPE_FROM_RELOAD = 4;
|
||||
private static final int NAVIGATION_TYPE_OTHER = 5;
|
||||
|
||||
// Analogous to Transient User Activation in blink (See
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation). We don't
|
||||
// want an "unattended" page to redirect to an app as the user is likely not expecting that.
|
||||
// However, historically there was no timeout like this for external navigation (and instead
|
||||
// touching the screen reset the navigation chain), so this timeout is very generous and should
|
||||
// allow for redirect chains.
|
||||
public static final long NAVIGATION_CHAIN_TIMEOUT_MILLIS = 15000;
|
||||
|
||||
private static class IntentState {
|
||||
final Intent mInitialIntent;
|
||||
final boolean mIsCustomTabIntent;
|
||||
@ -57,12 +67,14 @@ public class RedirectHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static class NavigationState {
|
||||
private class NavigationState {
|
||||
final int mInitialNavigationType;
|
||||
final boolean mHasUserStartedNonInitialNavigation;
|
||||
boolean mIsOnEffectiveRedirectChain;
|
||||
boolean mShouldNotOverrideUrlLoadingOnCurrentRedirectChain;
|
||||
boolean mShouldNotBlockOverrideUrlLoadingOnCurrentRedirectionChain;
|
||||
// TODO(https://crbug.com/1286053): Plumb through the user activation time from blink.
|
||||
final long mNavigationChainStartTime = currentRealtime();
|
||||
|
||||
NavigationState(int initialNavigationType, boolean hasUserStartedNonInitialNavigation) {
|
||||
mInitialNavigationType = initialNavigationType;
|
||||
@ -350,6 +362,16 @@ public class RedirectHandler {
|
||||
return mIntentState != null ? mIntentState.mInitialIntent : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the navigation chain has expired, meaning
|
||||
* {@link #NAVIGATION_CHAIN_TIMEOUT_MILLIS} milliseconds passed since a navigation initiated by
|
||||
* the user was started.
|
||||
*/
|
||||
public boolean isNavigationChainExpired() {
|
||||
return currentRealtime() - mNavigationState.mNavigationChainStartTime
|
||||
> NAVIGATION_CHAIN_TIMEOUT_MILLIS;
|
||||
}
|
||||
|
||||
public void maybeLogExternalRedirectBlockedWithMissingGesture() {
|
||||
if (mNavigationState.mInitialNavigationType
|
||||
== NAVIGATION_TYPE_FROM_LINK_WITHOUT_USER_GESTURE) {
|
||||
@ -362,4 +384,10 @@ public class RedirectHandler {
|
||||
"Android.Intent.BlockedExternalNavLastGestureTime", millisSinceLastGesture);
|
||||
}
|
||||
}
|
||||
|
||||
// Facilitates simulated waiting in tests.
|
||||
@VisibleForTesting
|
||||
public long currentRealtime() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@ -2543,6 +2544,36 @@ public class ExternalNavigationHandlerTest {
|
||||
START_OTHER_ACTIVITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
public void testExpiredNavigationChain() {
|
||||
mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME));
|
||||
|
||||
AtomicBoolean isExpired = new AtomicBoolean(false);
|
||||
RedirectHandler redirectHandler = new RedirectHandler() {
|
||||
@Override
|
||||
public boolean isNavigationChainExpired() {
|
||||
return isExpired.get();
|
||||
}
|
||||
};
|
||||
|
||||
// User clicks a link.
|
||||
redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0, false);
|
||||
|
||||
// Redirects to youtube with javascript simulated link click.
|
||||
redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1, false);
|
||||
checkUrl(YOUTUBE_MOBILE_URL)
|
||||
.withRedirectHandler(redirectHandler)
|
||||
.expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
|
||||
START_OTHER_ACTIVITY);
|
||||
|
||||
// Page takes > 15 seconds to redirect.
|
||||
isExpired.set(true);
|
||||
checkUrl(YOUTUBE_MOBILE_URL)
|
||||
.withRedirectHandler(redirectHandler)
|
||||
.expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
|
||||
}
|
||||
|
||||
private static List<ResolveInfo> makeResolveInfos(ResolveInfo... infos) {
|
||||
return Arrays.asList(infos);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public abstract class InterceptNavigationDelegate {
|
||||
*/
|
||||
@CalledByNative
|
||||
public abstract boolean shouldIgnoreNavigation(
|
||||
NavigationHandle navigationHandle, GURL escapedUrl, boolean applyUserGestureCarryover);
|
||||
NavigationHandle navigationHandle, GURL escapedUrl);
|
||||
|
||||
/**
|
||||
* This method is called for navigations to external protocols, which on Android are handled in
|
||||
@ -50,6 +50,14 @@ public abstract class InterceptNavigationDelegate {
|
||||
true /* isExternalProtocol */,
|
||||
0 /* navigationId - doesn't correspond to a native NavigationHandle*/,
|
||||
false /* isPageActivation */, false /* isReload */);
|
||||
shouldIgnoreNavigation(navigationHandle, escapedUrl, false);
|
||||
shouldIgnoreNavigation(navigationHandle, escapedUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a main frame requests a resource with a user gesture (eg. xhr,
|
||||
* fetch, etc.). The page may wish to redirect to an app after the resource requests completes,
|
||||
* which may be after blink user activation has expired.
|
||||
*/
|
||||
@CalledByNative
|
||||
protected void onResourceRequestWithGesture() {}
|
||||
}
|
||||
|
@ -32,8 +32,6 @@ namespace navigation_interception {
|
||||
|
||||
namespace {
|
||||
|
||||
const int kMaxValidityOfUserGestureCarryoverInSeconds = 10;
|
||||
|
||||
const void* const kInterceptNavigationDelegateUserDataKey =
|
||||
&kInterceptNavigationDelegateUserDataKey;
|
||||
|
||||
@ -119,16 +117,9 @@ bool InterceptNavigationDelegate::ShouldIgnoreNavigation(
|
||||
if (jdelegate.is_null())
|
||||
return false;
|
||||
|
||||
bool has_user_gesture = navigation_handle->HasUserGesture();
|
||||
bool apply_user_gesture_carryover =
|
||||
!has_user_gesture &&
|
||||
base::TimeTicks::Now() - last_user_gesture_carryover_timestamp_ <=
|
||||
base::Seconds(kMaxValidityOfUserGestureCarryoverInSeconds);
|
||||
|
||||
return Java_InterceptNavigationDelegate_shouldIgnoreNavigation(
|
||||
env, jdelegate, navigation_handle->GetJavaNavigationHandle(),
|
||||
url::GURLAndroid::FromNativeGURL(env, escaped_url),
|
||||
apply_user_gesture_carryover);
|
||||
url::GURLAndroid::FromNativeGURL(env, escaped_url));
|
||||
}
|
||||
|
||||
void InterceptNavigationDelegate::HandleExternalProtocolDialog(
|
||||
@ -153,8 +144,12 @@ void InterceptNavigationDelegate::HandleExternalProtocolDialog(
|
||||
initiating_origin ? initiating_origin->CreateJavaObject() : nullptr);
|
||||
}
|
||||
|
||||
void InterceptNavigationDelegate::UpdateLastUserGestureCarryoverTimestamp() {
|
||||
last_user_gesture_carryover_timestamp_ = base::TimeTicks::Now();
|
||||
void InterceptNavigationDelegate::OnResourceRequestWithGesture() {
|
||||
JNIEnv* env = base::android::AttachCurrentThread();
|
||||
ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
|
||||
if (jdelegate.is_null())
|
||||
return;
|
||||
Java_InterceptNavigationDelegate_onResourceRequestWithGesture(env, jdelegate);
|
||||
}
|
||||
|
||||
} // namespace navigation_interception
|
||||
|
@ -76,13 +76,12 @@ class InterceptNavigationDelegate : public base::SupportsUserData::Data {
|
||||
bool has_user_gesture,
|
||||
const absl::optional<url::Origin>& initiating_origin);
|
||||
|
||||
// Updates |last_user_gesture_carryover_timestamp_| when user gesture is
|
||||
// carried over.
|
||||
void UpdateLastUserGestureCarryoverTimestamp();
|
||||
// To be called when a main frame requests a resource with a user gesture (eg.
|
||||
// xrh, fetch, etc.)
|
||||
void OnResourceRequestWithGesture();
|
||||
|
||||
private:
|
||||
JavaObjectWeakGlobalRef weak_jdelegate_;
|
||||
base::TimeTicks last_user_gesture_carryover_timestamp_;
|
||||
bool escape_external_handler_value_ = false;
|
||||
};
|
||||
|
||||
|
@ -303,14 +303,6 @@ public class NavigationHandle {
|
||||
return mIsPageActivation;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(https://crbug.com/1310013): This is a hack, restoring M99 Chrome behavior for gesture
|
||||
* carryover on resource requests. To be deleted as soon as a better alternative is agreed upon.
|
||||
*/
|
||||
public void setUserGestureForCarryover(boolean hasUserGesture) {
|
||||
mHasUserGesture = hasUserGesture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this navigation was initiated by a page reload.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user