Commit 2e43a9bd authored by Colin Blundell's avatar Colin Blundell Committed by Commit Bot

[Android] Create RedirectHandler interface for ExternalNavHandler

We are in the midst of componentizing ExternalNavigationHandler.java for
reuse by WebLayer. The main remaining //chrome-level dependency is on
TabRedirectHandler.java. Unlike other dependencies, this dependency can
neither be easily componentized (it is essentially //chrome-level
functionality) nor can it be easily abstracted through
ExternalNavigationDelegate.java. The reason for the latter is that
the TabRedirectHandler instance is necessarily accessed through the
ExternalNavigationParams instance as it is tied to the navigation in
question, and if we were to remove all knowledge of it from
ExternalNavigationParams, there would be no straightforward way to
plumb it into //chrome's ExternalNavigationDelegateImpl.

This CL instead creates a RedirectHandler interface that contains the
methods needed by ExternalNavigationHandler. ExternalNavigationParams
and ExternalNavigationHandler are changed to talk in terms of this
interface, which TabRedirectHandler implements. This approach has the
additional benefit that it leaves existing logic coalesced within
ExternalNavigationHandler, which will make it easier to adapt this
logic in the WebLayer context as needed.

For this initial delegate interface, we have preserved the shape of the
interface to be the exact current usage by
ExternalNavigationHandler.java. Future work can refine the interface to
have it be as thin and logical as possible, potentially coalescing some
low-level methods into a smaller number of higher-level ones.

Bug: 1031465
Change-Id: I5b9568f371c248e8fd2d52429725ed0a44a098a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2095719
Commit-Queue: Colin Blundell <blundell@chromium.org>
Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748747}
parent a475ace6
......@@ -841,6 +841,7 @@ android_library("chrome_test_java") {
"//components/download/public/common:public_java",
"//components/embedder_support/android:content_view_java",
"//components/embedder_support/android:web_contents_delegate_java",
"//components/external_intents/android:java",
"//components/feature_engagement:feature_engagement_java",
"//components/gcm_driver/android:gcm_driver_java",
"//components/gcm_driver/instance_id/android:instance_id_driver_java",
......
......@@ -233,7 +233,7 @@ public class VrShell extends GvrLayout
mTabRedirectHandler = new TabRedirectHandler() {
@Override
public boolean shouldStayInChrome(boolean hasExternalProtocol) {
public boolean shouldStayInApp(boolean hasExternalProtocol) {
return !hasExternalProtocol;
}
};
......
......@@ -27,10 +27,10 @@ import org.chromium.base.Log;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.tab.TabRedirectHandler;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.embedder_support.util.UrlUtilities;
import org.chromium.components.external_intents.ExternalIntentsSwitches;
import org.chromium.components.external_intents.RedirectHandler;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.ui.base.PageTransition;
import org.chromium.url.URI;
......@@ -330,10 +330,10 @@ public class ExternalNavigationHandler {
*/
private boolean handleCCTRedirectsToInstantApps(ExternalNavigationParams params,
boolean isExternalProtocol, boolean incomingIntentRedirect) {
TabRedirectHandler handler = params.getRedirectHandler();
RedirectHandler handler = params.getRedirectHandler();
if (handler == null) return false;
if (handler.isFromCustomTabIntent() && !isExternalProtocol && incomingIntentRedirect
&& !handler.shouldNavigationTypeStayInChrome()
&& !handler.shouldNavigationTypeStayInApp()
&& mDelegate.maybeLaunchInstantApp(
params.getUrl(), params.getReferrerUrl(), true)) {
if (DEBUG) {
......@@ -346,11 +346,11 @@ public class ExternalNavigationHandler {
private boolean redirectShouldStayInChrome(
ExternalNavigationParams params, boolean isExternalProtocol, Intent targetIntent) {
TabRedirectHandler handler = params.getRedirectHandler();
RedirectHandler handler = params.getRedirectHandler();
if (handler == null) return false;
boolean shouldStayInChrome = handler.shouldStayInChrome(
boolean shouldStayInApp = handler.shouldStayInApp(
isExternalProtocol, mDelegate.isIntentForTrustedCallingApp(targetIntent));
if (shouldStayInChrome || handler.shouldNotOverrideUrlLoading()) {
if (shouldStayInApp || handler.shouldNotOverrideUrlLoading()) {
if (DEBUG) Log.i(TAG, "RedirectHandler decision");
return true;
}
......@@ -999,7 +999,7 @@ public class ExternalNavigationHandler {
// status in one shot. In order to prevent this scenario, we notify redirection
// handler that redirection from the current navigation should stay in Chrome.
if (params.getRedirectHandler() != null) {
params.getRedirectHandler().setShouldNotOverrideUrlLoadingUntilNewUrlLoading();
params.getRedirectHandler().setShouldNotOverrideUrlLoadingOnCurrentRedirectChain();
}
if (DEBUG) Log.i(TAG, "clobberCurrentTab called");
return mDelegate.clobberCurrentTab(browserFallbackUrl, params.getReferrerUrl());
......
......@@ -4,7 +4,7 @@
package org.chromium.chrome.browser.externalnav;
import org.chromium.chrome.browser.tab.TabRedirectHandler;
import org.chromium.components.external_intents.RedirectHandler;
/**
* A container object for passing navigation parameters to {@link ExternalNavigationHandler}.
......@@ -29,7 +29,7 @@ public class ExternalNavigationParams {
private final boolean mApplicationMustBeInForeground;
/** A redirect handler. */
private final TabRedirectHandler mRedirectHandler;
private final RedirectHandler mRedirectHandler;
/** Whether the intent should force a new tab to open. */
private final boolean mOpenInNewTab;
......@@ -57,7 +57,7 @@ public class ExternalNavigationParams {
private ExternalNavigationParams(String url, boolean isIncognito, String referrerUrl,
int pageTransition, boolean isRedirect, boolean appMustBeInForeground,
TabRedirectHandler redirectHandler, boolean openInNewTab,
RedirectHandler redirectHandler, boolean openInNewTab,
boolean isBackgroundTabNavigation, boolean isMainFrame, String nativeClientPackageName,
boolean hasUserGesture,
boolean shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent) {
......@@ -108,7 +108,7 @@ public class ExternalNavigationParams {
}
/** @return The redirect handler. */
public TabRedirectHandler getRedirectHandler() {
public RedirectHandler getRedirectHandler() {
return mRedirectHandler;
}
......@@ -172,7 +172,7 @@ public class ExternalNavigationParams {
private boolean mApplicationMustBeInForeground;
/** A redirect handler. */
private TabRedirectHandler mRedirectHandler;
private RedirectHandler mRedirectHandler;
/** Whether the intent should force a new tab to open. */
private boolean mOpenInNewTab;
......@@ -219,7 +219,7 @@ public class ExternalNavigationParams {
}
/** Sets a tab redirect handler. */
public Builder setRedirectHandler(TabRedirectHandler handler) {
public Builder setRedirectHandler(RedirectHandler handler) {
mRedirectHandler = handler;
return this;
}
......
......@@ -21,6 +21,7 @@ import org.chromium.base.UserDataHost;
import org.chromium.chrome.browser.LaunchIntentDispatcher;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.components.external_intents.RedirectHandler;
import org.chromium.ui.base.PageTransition;
import java.util.HashSet;
......@@ -29,7 +30,7 @@ import java.util.List;
/**
* This class contains the logic to determine effective navigation/redirect.
*/
public class TabRedirectHandler extends EmptyTabObserver implements UserData {
public class TabRedirectHandler extends EmptyTabObserver implements UserData, RedirectHandler {
private static final Class<TabRedirectHandler> USER_DATA_KEY = TabRedirectHandler.class;
/**
* An invalid entry index.
......@@ -55,7 +56,7 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
private int mInitialNavigationType;
private int mLastCommittedEntryIndexBeforeStartingNavigation;
private boolean mShouldNotOverrideUrlLoadingUntilNewUrlLoading;
private boolean mShouldNotOverrideUrlLoadingOnCurrentRedirectChain;
/**
* Returns {@link TabRedirectHandler} that hangs on to a given {@link Tab}.
......@@ -167,11 +168,12 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
mInitialNavigationType = NAVIGATION_TYPE_NONE;
mIsOnEffectiveRedirectChain = false;
mLastCommittedEntryIndexBeforeStartingNavigation = 0;
mShouldNotOverrideUrlLoadingUntilNewUrlLoading = false;
mShouldNotOverrideUrlLoadingOnCurrentRedirectChain = false;
}
public void setShouldNotOverrideUrlLoadingUntilNewUrlLoading() {
mShouldNotOverrideUrlLoadingUntilNewUrlLoading = true;
@Override
public void setShouldNotOverrideUrlLoadingOnCurrentRedirectChain() {
mShouldNotOverrideUrlLoadingOnCurrentRedirectChain = true;
}
/**
......@@ -232,7 +234,7 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
}
mIsOnEffectiveRedirectChain = false;
mLastCommittedEntryIndexBeforeStartingNavigation = lastCommittedEntryIndex;
mShouldNotOverrideUrlLoadingUntilNewUrlLoading = false;
mShouldNotOverrideUrlLoadingOnCurrentRedirectChain = false;
} else if (mInitialNavigationType != NAVIGATION_TYPE_NONE) {
// Redirect chain starts from the second url loading.
mIsOnEffectiveRedirectChain = true;
......@@ -242,6 +244,7 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
/**
* @return whether on effective intent redirect chain or not.
*/
@Override
public boolean isOnEffectiveIntentRedirectChain() {
return mInitialNavigationType == NAVIGATION_TYPE_FROM_INTENT && mIsOnEffectiveRedirectChain;
}
......@@ -250,8 +253,8 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
* @param hasExternalProtocol whether the destination URI has an external protocol or not.
* @return whether we should stay in Chrome or not.
*/
public boolean shouldStayInChrome(boolean hasExternalProtocol) {
return shouldStayInChrome(hasExternalProtocol, false);
public boolean shouldStayInApp(boolean hasExternalProtocol) {
return shouldStayInApp(hasExternalProtocol, false);
}
/**
......@@ -260,22 +263,23 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
* Chrome.
* @return whether we should stay in Chrome or not.
*/
public boolean shouldStayInChrome(boolean hasExternalProtocol,
boolean isForTrustedCallingApp) {
@Override
public boolean shouldStayInApp(boolean hasExternalProtocol, boolean isForTrustedCallingApp) {
// http://crbug/424029 : Need to stay in Chrome for an intent heading explicitly to Chrome.
// http://crbug/881740 : Relax stay in Chrome restriction for Custom Tabs.
return (mIsInitialIntentHeadingToChrome && !hasExternalProtocol)
|| shouldNavigationTypeStayInChrome(isForTrustedCallingApp);
|| shouldNavigationTypeStayInApp(isForTrustedCallingApp);
}
/**
* @return Whether the current navigation is of the type that should always stay in Chrome.
*/
public boolean shouldNavigationTypeStayInChrome() {
return shouldNavigationTypeStayInChrome(false);
@Override
public boolean shouldNavigationTypeStayInApp() {
return shouldNavigationTypeStayInApp(false);
}
private boolean shouldNavigationTypeStayInChrome(boolean isForTrustedCallingApp) {
private boolean shouldNavigationTypeStayInApp(boolean isForTrustedCallingApp) {
// http://crbug.com/162106: Never leave Chrome from a refresh.
if (mInitialNavigationType == NAVIGATION_TYPE_FROM_RELOAD) return true;
......@@ -290,6 +294,7 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
/**
* @return Whether this navigation is initiated by a Custom Tabs {@link Intent}.
*/
@Override
public boolean isFromCustomTabIntent() {
return mIsCustomTabIntent;
}
......@@ -297,6 +302,7 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
/**
* @return whether navigation is from a user's typing or not.
*/
@Override
public boolean isNavigationFromUserTyping() {
return mInitialNavigationType == NAVIGATION_TYPE_FROM_USER_TYPING;
}
......@@ -304,8 +310,9 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
/**
* @return whether we should stay in Chrome or not.
*/
@Override
public boolean shouldNotOverrideUrlLoading() {
return mShouldNotOverrideUrlLoadingUntilNewUrlLoading;
return mShouldNotOverrideUrlLoadingOnCurrentRedirectChain;
}
/**
......@@ -325,6 +332,7 @@ public class TabRedirectHandler extends EmptyTabObserver implements UserData {
/**
* @return whether |intent| has a new resolver against |mIntentHistory| or not.
*/
@Override
public boolean hasNewResolver(List<ResolveInfo> resolvingInfos) {
if (mInitialIntent == null) {
return !resolvingInfos.isEmpty();
......
......@@ -208,6 +208,6 @@ public class InterceptNavigationDelegateTest {
Assert.assertTrue(mNavParamHistory.get(2).isExternalProtocol);
Assert.assertFalse(mNavParamHistory.get(2).isMainFrame);
Assert.assertTrue(
mExternalNavParamHistory.get(2).getRedirectHandler().shouldStayInChrome(true));
mExternalNavParamHistory.get(2).getRedirectHandler().shouldStayInApp(true, false));
}
}
......@@ -5,5 +5,8 @@
import("//build/config/android/rules.gni")
android_library("java") {
sources = [ "java/src/org/chromium/components/external_intents/ExternalIntentsSwitches.java" ]
sources = [
"java/src/org/chromium/components/external_intents/ExternalIntentsSwitches.java",
"java/src/org/chromium/components/external_intents/RedirectHandler.java",
]
}
// Copyright 2020 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.components.external_intents;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import java.util.List;
/**
* This interface allows the embedder to supply the logic that ExternalNavigationHandler.java uses
* to determine effective navigation/redirect while handling a given intent.
*/
public interface RedirectHandler {
/**
* @return whether on effective intent redirect chain or not.
*/
boolean isOnEffectiveIntentRedirectChain();
/**
* @param hasExternalProtocol whether the destination URI has an external protocol or not.
* @param isForTrustedCallingApp whether the app we would launch to is trusted and what launched
* this embedder.
* @return whether we should stay in this app or not.
*/
boolean shouldStayInApp(boolean hasExternalProtocol, boolean isForTrustedCallingApp);
/**
* @return Whether the current navigation is of the type that should always stay in this app.
*/
boolean shouldNavigationTypeStayInApp();
/**
* @return Whether this navigation is initiated by a Custom Tab {@link Intent}.
*/
boolean isFromCustomTabIntent();
/**
* @return whether navigation is from a user's typing or not.
*/
boolean isNavigationFromUserTyping();
/**
* Will cause shouldNotOverrideUrlLoading() to return true until a new user-initiated navigation
* occurs. The embedder implementation is responsible for enforcing these semantics.
*/
void setShouldNotOverrideUrlLoadingOnCurrentRedirectChain();
/**
* @return whether we should stay in this app or not.
*/
boolean shouldNotOverrideUrlLoading();
/**
* @return whether |resolvingInfos| contains a new resolver for the intent on which this
* redirect handler was operating, compared to the resolvers shown when the user chose this app
* to handle that intent. Relevant only if this app is one that handles incoming user
* intents.
*/
boolean hasNewResolver(List<ResolveInfo> resolvingInfos);
}
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