summaryrefslogtreecommitdiff
path: root/chromium/components/external_intents/android
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/components/external_intents/android
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/external_intents/android')
-rw-r--r--chromium/components/external_intents/android/BUILD.gn5
-rw-r--r--chromium/components/external_intents/android/external_intents_feature_list.cc1
-rw-r--r--chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java10
-rw-r--r--chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java182
-rw-r--r--chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationParams.java64
-rw-r--r--chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java4
-rw-r--r--chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java176
-rw-r--r--chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java26
8 files changed, 364 insertions, 104 deletions
diff --git a/chromium/components/external_intents/android/BUILD.gn b/chromium/components/external_intents/android/BUILD.gn
index 530cfa2c449..f0688b0e6ed 100644
--- a/chromium/components/external_intents/android/BUILD.gn
+++ b/chromium/components/external_intents/android/BUILD.gn
@@ -25,9 +25,11 @@ android_library("java") {
"//components/embedder_support/android:util_java",
"//components/navigation_interception/android:navigation_interception_java",
"//content/public/android:content_java",
+ "//services/network/public/mojom:mojom_java",
"//third_party/android_deps:androidx_annotation_annotation_java",
"//ui/android:ui_java",
"//url:gurl_java",
+ "//url:origin_java",
]
}
@@ -72,7 +74,10 @@ android_library("javatests") {
"//base:base_java_test_support",
"//content/public/test/android:content_java_test_support",
"//third_party/android_deps:androidx_core_core_java",
+ "//third_party/android_sdk:android_test_mock_java",
"//third_party/android_sdk/androidx_browser:androidx_browser_java",
+ "//third_party/android_support_test_runner:runner_java",
+ "//third_party/junit",
"//ui/android:ui_java",
]
}
diff --git a/chromium/components/external_intents/android/external_intents_feature_list.cc b/chromium/components/external_intents/android/external_intents_feature_list.cc
index 71d82a494be..91a4c0e7edc 100644
--- a/chromium/components/external_intents/android/external_intents_feature_list.cc
+++ b/chromium/components/external_intents/android/external_intents_feature_list.cc
@@ -9,6 +9,7 @@
#include <string>
#include "base/android/jni_string.h"
+#include "base/notreached.h"
#include "components/external_intents/android/jni_headers/ExternalIntentsFeatureList_jni.h"
namespace external_intents {
diff --git a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java
index 156128517d4..dd8a259c4f4 100644
--- a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java
+++ b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java
@@ -9,11 +9,13 @@ import android.content.Intent;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.Origin;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -133,8 +135,12 @@ public interface ExternalNavigationDelegate {
*/
void maybeAdjustInstantAppExtras(Intent intent, boolean isIntentToInstantApp);
- /** Invoked for intents with user gestures and records the user gesture if desired. */
- void maybeSetUserGesture(Intent intent);
+ /**
+ * Invoked for intents with request metadata such as user gesture, whether request is renderer
+ * initiated and the initiator origin. Records the information if desired.
+ */
+ void maybeSetRequestMetadata(Intent intent, boolean hasUserGesture, boolean isRendererInitiated,
+ @Nullable Origin initiatorOrigin);
/**
* Records the pending incognito URL if desired. Called only if the
diff --git a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index b1b9bffa4b1..69f43cc8cfa 100644
--- a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -160,6 +160,17 @@ public class ExternalNavigationHandler {
@VisibleForTesting
static final String INTENT_ACTION_HISTOGRAM = "Android.Intent.OverrideUrlLoadingIntentAction";
+ // Helper class to return a boolean by reference.
+ private static class MutableBoolean {
+ private Boolean mValue = null;
+ public void set(boolean value) {
+ mValue = value;
+ }
+ public Boolean get() {
+ return mValue;
+ }
+ }
+
private final ExternalNavigationDelegate mDelegate;
/**
@@ -217,9 +228,16 @@ public class ExternalNavigationHandler {
&& !UrlUtilities.isValidForIntentFallbackNavigation(browserFallbackUrl)) {
browserFallbackUrl = null;
}
+
+ // TODO(https://crbug.com/1096099): Refactor shouldOverrideUrlLoadingInternal, splitting it
+ // up to separate out the notions wanting to fire an external intent vs being able to.
+ MutableBoolean canLaunchExternalFallbackResult = new MutableBoolean();
+
long time = SystemClock.elapsedRealtime();
@OverrideUrlLoadingResult
- int result = shouldOverrideUrlLoadingInternal(params, targetIntent, browserFallbackUrl);
+ int result = shouldOverrideUrlLoadingInternal(
+ params, targetIntent, browserFallbackUrl, canLaunchExternalFallbackResult);
+ assert canLaunchExternalFallbackResult.get() != null;
RecordHistogram.recordTimesHistogram(
"Android.StrictMode.OverrideUrlLoadingTime", SystemClock.elapsedRealtime() - time);
@@ -236,31 +254,64 @@ public class ExternalNavigationHandler {
&& (params.getRedirectHandler() == null
// For instance, if this is a chained fallback URL, we ignore it.
|| !params.getRedirectHandler().shouldNotOverrideUrlLoading())) {
- result = handleFallbackUrl(params, targetIntent, browserFallbackUrl);
+ result = handleFallbackUrl(params, targetIntent, browserFallbackUrl,
+ canLaunchExternalFallbackResult.get());
}
if (DEBUG) printDebugShouldOverrideUrlLoadingResult(result);
return result;
}
- private @OverrideUrlLoadingResult int handleFallbackUrl(
- ExternalNavigationParams params, Intent targetIntent, String browserFallbackUrl) {
+ private @OverrideUrlLoadingResult int handleFallbackUrl(ExternalNavigationParams params,
+ Intent targetIntent, String browserFallbackUrl, boolean canLaunchExternalFallback) {
if (mDelegate.isIntentToInstantApp(targetIntent)) {
RecordHistogram.recordEnumeratedHistogram("Android.InstantApps.DirectInstantAppsIntent",
AiaIntent.FALLBACK_USED, AiaIntent.NUM_ENTRIES);
}
- // Launch WebAPK if it can handle the URL.
- try {
- Intent intent = Intent.parseUri(browserFallbackUrl, Intent.URI_INTENT_SCHEME);
- sanitizeQueryIntentActivitiesIntent(intent);
- List<ResolveInfo> resolvingInfos = queryIntentActivities(intent);
- if (!isAlreadyInTargetWebApk(resolvingInfos, params)
- && launchWebApkIfSoleIntentHandler(resolvingInfos, intent)) {
- return OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT;
+
+ if (canLaunchExternalFallback) {
+ if (shouldBlockAllExternalAppLaunches(params) || params.isIncognito()) {
+ throw new SecurityException("Context is not allowed to launch an external app.");
}
- } catch (Exception e) {
- if (DEBUG) Log.i(TAG, "Could not parse fallback url as intent");
+ // Launch WebAPK if it can handle the URL.
+ try {
+ Intent intent = Intent.parseUri(browserFallbackUrl, Intent.URI_INTENT_SCHEME);
+ sanitizeQueryIntentActivitiesIntent(intent);
+ List<ResolveInfo> resolvingInfos = queryIntentActivities(intent);
+ if (!isAlreadyInTargetWebApk(resolvingInfos, params)
+ && launchWebApkIfSoleIntentHandler(resolvingInfos, intent)) {
+ return OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT;
+ }
+ } catch (Exception e) {
+ if (DEBUG) Log.i(TAG, "Could not parse fallback url as intent");
+ }
+
+ // If the fallback URL is a link to Play Store, send the user to Play Store app
+ // instead: crbug.com/638672.
+ Pair<String, String> appInfo = maybeGetPlayStoreAppIdAndReferrer(browserFallbackUrl);
+ if (appInfo != null) {
+ String marketReferrer = TextUtils.isEmpty(appInfo.second)
+ ? ContextUtils.getApplicationContext().getPackageName()
+ : appInfo.second;
+ return sendIntentToMarket(appInfo.first, marketReferrer, params);
+ }
+ }
+
+ // For subframes, we don't support fallback url for now.
+ // http://crbug.com/364522.
+ if (!params.isMainFrame()) {
+ if (DEBUG) Log.i(TAG, "Don't support fallback url in subframes");
+ return OverrideUrlLoadingResult.NO_OVERRIDE;
}
- return clobberCurrentTabWithFallbackUrl(browserFallbackUrl, params);
+
+ // NOTE: any further redirection from fall-back URL should not override URL loading.
+ // Otherwise, it can be used in chain for fingerprinting multiple app installation
+ // status in one shot. In order to prevent this scenario, we notify redirection
+ // handler that redirection from the current navigation should stay in this app.
+ if (params.getRedirectHandler() != null) {
+ params.getRedirectHandler().setShouldNotOverrideUrlLoadingOnCurrentRedirectChain();
+ }
+ if (DEBUG) Log.i(TAG, "clobberCurrentTab called");
+ return clobberCurrentTab(browserFallbackUrl, params.getReferrerUrl());
}
private void printDebugShouldOverrideUrlLoadingResult(int result) {
@@ -300,6 +351,19 @@ public class ExternalNavigationHandler {
}
/**
+ * https://crbug.com/1094442: Don't allow any external navigation on AUTO_SUBFRAME navigation
+ * (eg. initial ad frame navigation).
+ */
+ private boolean blockExternalNavFromAutoSubframe(ExternalNavigationParams params) {
+ int pageTransitionCore = params.getPageTransition() & PageTransition.CORE_MASK;
+ if (pageTransitionCore == PageTransition.AUTO_SUBFRAME) {
+ if (DEBUG) Log.i(TAG, "Auto navigation in subframe");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* http://crbug.com/441284 : Disallow firing external intent while the app is in the background.
*/
private boolean blockExternalNavWhileBackgrounded(ExternalNavigationParams params) {
@@ -651,6 +715,30 @@ public class ExternalNavigationHandler {
}
/**
+ * Intent URIs leads to creating intents that chrome would use for firing external navigations
+ * via Android. Android throws an exception [1] when an application exposes a file:// Uri to
+ * another app.
+ *
+ * This method checks if the |targetIntent| contains the file:// scheme in its data.
+ *
+ * [1]: https://developer.android.com/reference/android/os/FileUriExposedException
+ */
+ private boolean hasFileSchemeInIntentURI(Intent targetIntent, boolean hasIntentScheme) {
+ // We are only concerned with targetIntent that was generated due to intent:// schemes only.
+ if (!hasIntentScheme) return false;
+
+ Uri data = targetIntent.getData();
+
+ if (data == null || data.getScheme() == null) return false;
+
+ if (data.getScheme().equalsIgnoreCase(UrlConstants.FILE_SCHEME)) {
+ if (DEBUG) Log.i(TAG, "Intent navigation to file: URI");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Special case - It makes no sense to use an external application for a YouTube
* pairing code URL, since these match the current tab with a device (Chromecast
* or similar) it is supposed to be controlling. Using a different application
@@ -851,7 +939,8 @@ public class ExternalNavigationHandler {
AiaIntent.SERP, AiaIntent.NUM_ENTRIES);
}
- if (params.hasUserGesture()) mDelegate.maybeSetUserGesture(targetIntent);
+ mDelegate.maybeSetRequestMetadata(targetIntent, params.hasUserGesture(),
+ params.isRendererInitiated(), params.getInitiatorOrigin());
}
private @OverrideUrlLoadingResult int handleExternalIncognitoIntent(Intent targetIntent,
@@ -940,15 +1029,20 @@ public class ExternalNavigationHandler {
return false;
}
+ // Check if we're navigating under conditions that should never launch an external app.
+ private boolean shouldBlockAllExternalAppLaunches(ExternalNavigationParams params) {
+ return blockExternalNavFromAutoSubframe(params) || blockExternalNavWhileBackgrounded(params)
+ || blockExternalNavFromBackgroundTab(params) || ignoreBackForwardNav(params);
+ }
+
private @OverrideUrlLoadingResult int shouldOverrideUrlLoadingInternal(
ExternalNavigationParams params, Intent targetIntent,
- @Nullable String browserFallbackUrl) {
+ @Nullable String browserFallbackUrl, MutableBoolean canLaunchExternalFallbackResult) {
sanitizeQueryIntentActivitiesIntent(targetIntent);
+ // Don't allow external fallback URLs by default.
+ canLaunchExternalFallbackResult.set(false);
- if (blockExternalNavWhileBackgrounded(params) || blockExternalNavFromBackgroundTab(params)
- || ignoreBackForwardNav(params)) {
- return OverrideUrlLoadingResult.NO_OVERRIDE;
- }
+ if (shouldBlockAllExternalAppLaunches(params)) return OverrideUrlLoadingResult.NO_OVERRIDE;
if (handleWithAutofillAssistant(params, targetIntent, browserFallbackUrl)) {
return OverrideUrlLoadingResult.NO_OVERRIDE;
@@ -1016,6 +1110,10 @@ public class ExternalNavigationHandler {
return OverrideUrlLoadingResult.NO_OVERRIDE;
}
+ if (hasFileSchemeInIntentURI(targetIntent, hasIntentScheme)) {
+ return OverrideUrlLoadingResult.NO_OVERRIDE;
+ }
+
if (isYoutubePairingCode(params)) return OverrideUrlLoadingResult.NO_OVERRIDE;
if (shouldStayInIncognito(params, isExternalProtocol)) {
@@ -1026,6 +1124,10 @@ public class ExternalNavigationHandler {
if (hasIntentScheme) recordIntentActionMetrics(targetIntent);
+ // From this point on, we have determined it is safe to launch an External App from a
+ // fallback URL, provided the user isn't in incognito.
+ if (!params.isIncognito()) canLaunchExternalFallbackResult.set(true);
+
Intent debugIntent = new Intent(targetIntent);
List<ResolveInfo> resolvingInfos = queryIntentActivities(targetIntent);
if (resolvingInfos.isEmpty()) {
@@ -1154,44 +1256,6 @@ public class ExternalNavigationHandler {
}
/**
- * Clobber the current tab with fallback URL.
- *
- * @param browserFallbackUrl The fallback URL.
- * @param params The external navigation params.
- * @return {@link OverrideUrlLoadingResult} if the tab was clobbered, or we launched an
- * intent.
- */
- private @OverrideUrlLoadingResult int clobberCurrentTabWithFallbackUrl(
- String browserFallbackUrl, ExternalNavigationParams params) {
- // If the fallback URL is a link to Play Store, send the user to Play Store app
- // instead: crbug.com/638672.
- Pair<String, String> appInfo = maybeGetPlayStoreAppIdAndReferrer(browserFallbackUrl);
- if (appInfo != null) {
- String marketReferrer = TextUtils.isEmpty(appInfo.second)
- ? ContextUtils.getApplicationContext().getPackageName()
- : appInfo.second;
- return sendIntentToMarket(appInfo.first, marketReferrer, params);
- }
-
- // For subframes, we don't support fallback url for now.
- // http://crbug.com/364522.
- if (!params.isMainFrame()) {
- if (DEBUG) Log.i(TAG, "Don't support fallback url in subframes");
- return OverrideUrlLoadingResult.NO_OVERRIDE;
- }
-
- // NOTE: any further redirection from fall-back URL should not override URL loading.
- // Otherwise, it can be used in chain for fingerprinting multiple app installation
- // status in one shot. In order to prevent this scenario, we notify redirection
- // handler that redirection from the current navigation should stay in this app.
- if (params.getRedirectHandler() != null) {
- params.getRedirectHandler().setShouldNotOverrideUrlLoadingOnCurrentRedirectChain();
- }
- if (DEBUG) Log.i(TAG, "clobberCurrentTab called");
- return clobberCurrentTab(browserFallbackUrl, params.getReferrerUrl());
- }
-
- /**
* If the given URL is to Google Play, extracts the package name and referrer tracking code
* from the {@param url} and returns as a Pair in that order. Otherwise returns null.
*/
diff --git a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationParams.java b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationParams.java
index f4d40512867..fe172dadec4 100644
--- a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationParams.java
+++ b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationParams.java
@@ -4,6 +4,10 @@
package org.chromium.components.external_intents;
+import androidx.annotation.Nullable;
+
+import org.chromium.url.Origin;
+
/**
* A container object for passing navigation parameters to {@link ExternalNavigationHandler}.
*/
@@ -53,12 +57,22 @@ public class ExternalNavigationParams {
*/
private final boolean mShouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent;
+ /**
+ * Whether the navigation is initiated by the renderer.
+ */
+ private boolean mIsRendererInitiated;
+
+ /**
+ * The origin that initiates the navigation, could be null.
+ */
+ private Origin mInitiatorOrigin;
+
private ExternalNavigationParams(String url, boolean isIncognito, String referrerUrl,
int pageTransition, boolean isRedirect, boolean appMustBeInForeground,
RedirectHandler redirectHandler, boolean openInNewTab,
boolean isBackgroundTabNavigation, boolean isMainFrame, String nativeClientPackageName,
- boolean hasUserGesture,
- boolean shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent) {
+ boolean hasUserGesture, boolean shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent,
+ boolean isRendererInitiated, @Nullable Origin initiatorOrigin) {
mUrl = url;
mIsIncognito = isIncognito;
mPageTransition = pageTransition;
@@ -73,6 +87,8 @@ public class ExternalNavigationParams {
mHasUserGesture = hasUserGesture;
mShouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent =
shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent;
+ mIsRendererInitiated = isRendererInitiated;
+ mInitiatorOrigin = initiatorOrigin;
}
/** @return The URL to potentially open externally. */
@@ -149,6 +165,21 @@ public class ExternalNavigationParams {
return mShouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent;
}
+ /**
+ * @return Whether the navigation is initiated by renderer.
+ */
+ public boolean isRendererInitiated() {
+ return mIsRendererInitiated;
+ }
+
+ /**
+ * @return The origin that initiates the navigation.
+ */
+ @Nullable
+ public Origin getInitiatorOrigin() {
+ return mInitiatorOrigin;
+ }
+
/** The builder for {@link ExternalNavigationParams} objects. */
public static class Builder {
/** The URL which we are navigating to. */
@@ -196,6 +227,16 @@ public class ExternalNavigationParams {
*/
private boolean mShouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent;
+ /**
+ * Whether the navigation is initiated by the renderer.
+ */
+ private boolean mIsRendererInitiated;
+
+ /**
+ * The origin that initiates the navigation, could be null.
+ */
+ private Origin mInitiatorOrigin;
+
public Builder(String url, boolean isIncognito) {
mUrl = url;
mIsIncognito = isIncognito;
@@ -261,12 +302,29 @@ public class ExternalNavigationParams {
return this;
}
+ /**
+ * Sets whether the navigation is initiated by renderer.
+ */
+ public Builder setIsRendererInitiated(boolean v) {
+ mIsRendererInitiated = v;
+ return this;
+ }
+
+ /**
+ * Sets the origin that initiates the navigation.
+ */
+ public Builder setInitiatorOrigin(@Nullable Origin v) {
+ mInitiatorOrigin = v;
+ return this;
+ }
+
/** @return A fully constructed {@link ExternalNavigationParams} object. */
public ExternalNavigationParams build() {
return new ExternalNavigationParams(mUrl, mIsIncognito, mReferrerUrl, mPageTransition,
mIsRedirect, mApplicationMustBeInForeground, mRedirectHandler, mOpenInNewTab,
mIsBackgroundTabNavigation, mIsMainFrame, mNativeClientPackageName,
- mHasUserGesture, mShouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent);
+ mHasUserGesture, mShouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent,
+ mIsRendererInitiated, mInitiatorOrigin);
}
}
}
diff --git a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
index dad8bbfe811..686721169fa 100644
--- a/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
+++ b/chromium/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
@@ -180,7 +180,9 @@ public class InterceptNavigationDelegateImpl implements InterceptNavigationDeleg
.setIsMainFrame(navigationParams.isMainFrame)
.setHasUserGesture(navigationParams.hasUserGesture)
.setShouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent(
- shouldCloseTab && navigationParams.isMainFrame);
+ shouldCloseTab && navigationParams.isMainFrame)
+ .setIsRendererInitiated(navigationParams.isRendererInitiated)
+ .setInitiatorOrigin(navigationParams.initiatorOrigin);
}
/**
diff --git a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
index 867a67a22b6..33090212178 100644
--- a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
+++ b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
+// 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.
@@ -19,14 +19,13 @@ import android.os.Bundle;
import android.os.SystemClock;
import android.provider.Browser;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
import android.test.mock.MockPackageManager;
import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,14 +33,15 @@ import org.chromium.base.ContextUtils;
import org.chromium.base.IntentUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.DisableIf;
-import org.chromium.components.external_intents.ExternalNavigationDelegate.StartActivityIfNeededResult;
import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.browser.test.NativeLibraryTestRule;
+import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.Origin;
import java.net.URISyntaxException;
import java.util.ArrayList;
@@ -56,11 +56,9 @@ import java.util.regex.Pattern;
// clang-format off
@DisableIf.Build(message = "Flaky on K - see https://crbug.com/851444",
sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP)
+@Batch(Batch.UNIT_TESTS)
public class ExternalNavigationHandlerTest {
// clang-format on
- @Rule
- public final NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
-
// Expectations
private static final int IGNORE = 0x0;
private static final int START_INCOGNITO = 0x1;
@@ -162,7 +160,7 @@ public class ExternalNavigationHandlerTest {
mContext = new TestContext(InstrumentationRegistry.getTargetContext(), mDelegate);
ContextUtils.initApplicationContextForTests(mContext);
- mNativeLibraryTestRule.loadNativeLibraryNoBrowserProcess();
+ NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
}
@Test
@@ -331,16 +329,13 @@ public class ExternalNavigationHandlerTest {
@SmallTest
public void testIgnore() {
// Ensure the following URLs are not broadcast for external navigation.
- String urlsToIgnore[] = new String[] {
- "about:test",
+ String urlsToIgnore[] = new String[] {"about:test",
"content:test", // Content URLs should not be exposed outside of Chrome.
- "chrome://history",
- "chrome-native://newtab",
- "devtools://foo",
+ "chrome://history", "chrome-native://newtab", "devtools://foo",
"intent:chrome-urls#Intent;package=com.android.chrome;scheme=about;end;",
"intent:chrome-urls#Intent;package=com.android.chrome;scheme=chrome;end;",
"intent://com.android.chrome.FileProvider/foo.html#Intent;scheme=content;end;",
- };
+ "intent:///x.mhtml#Intent;package=com.android.chrome;action=android.intent.action.VIEW;scheme=file;end;"};
for (String url : urlsToIgnore) {
checkUrl(url).expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE);
checkUrl(url).withIsIncognito(true).expecting(
@@ -945,16 +940,52 @@ public class ExternalNavigationHandlerTest {
@Test
@SmallTest
+ public void testFallbackUrl_SubframeFallbackToMarketApp() {
+ mDelegate.setCanResolveActivityForExternalSchemes(false);
+
+ RedirectHandler redirectHandler = RedirectHandler.create();
+ redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0);
+ String intent = "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;"
+ + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "="
+ + "https://play.google.com/store/apps/details?id=com.imdb.mobile"
+ + "&referrer=mypage;end";
+ checkUrl(intent)
+ .withIsMainFrame(false)
+ .withHasUserGesture(true)
+ .withRedirectHandler(redirectHandler)
+ .withPageTransition(PageTransition.LINK)
+ .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
+ Assert.assertEquals("market://details?id=com.imdb.mobile&referrer=mypage",
+ mDelegate.startActivityIntent.getDataString());
+
+ redirectHandler = RedirectHandler.create();
+ redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0);
+ String intentBadUrl = "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;"
+ + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "="
+ + "https://play.google.com/store/search?q=pub:imdb;end";
+ checkUrl(intentBadUrl)
+ .withIsMainFrame(false)
+ .withHasUserGesture(true)
+ .withRedirectHandler(redirectHandler)
+ .withPageTransition(PageTransition.LINK)
+ .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE);
+ }
+
+ @Test
+ @SmallTest
public void testFallbackUrl_RedirectToIntentToMarket() {
+ mDelegate.setCanResolveActivityForExternalSchemes(false);
+
RedirectHandler redirectHandler = RedirectHandler.create();
- redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0);
+ redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0);
checkUrl("http://goo.gl/abcdefg")
- .withPageTransition(PageTransition.TYPED)
+ .withPageTransition(PageTransition.LINK)
.withRedirectHandler(redirectHandler)
.expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE);
- redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0);
+ redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, true, 0, 0);
String realIntent = "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;"
+ "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "="
+ "https://play.google.com/store/apps/details?id=com.imdb.mobile"
@@ -973,6 +1004,44 @@ public class ExternalNavigationHandlerTest {
@Test
@SmallTest
+ public void testFallbackUrl_DontFallbackForAutoSubframe() {
+ // IMDB app isn't installed.
+ mDelegate.setCanResolveActivityForExternalSchemes(false);
+
+ mDelegate.add(new IntentActivity(IMDB_WEBPAGE_FOR_TOM_HANKS, WEBAPK_PACKAGE_NAME)
+ .withIsWebApk(true));
+
+ RedirectHandler redirectHandler = RedirectHandler.create();
+ redirectHandler.updateNewUrlLoading(PageTransition.AUTO_SUBFRAME, true, true, 0, 0);
+
+ checkUrl(INTENT_URL_WITH_FALLBACK_URL)
+ .withIsMainFrame(false)
+ .withHasUserGesture(true)
+ .withRedirectHandler(redirectHandler)
+ .withPageTransition(PageTransition.AUTO_SUBFRAME)
+ .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS)
+ .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE);
+ }
+
+ @Test
+ @SmallTest
+ public void testFallbackUrl_NoExternalFallbackWithoutGesture() {
+ mDelegate.add(new IntentActivity(IMDB_WEBPAGE_FOR_TOM_HANKS, WEBAPK_PACKAGE_NAME)
+ .withIsWebApk(true));
+
+ RedirectHandler redirectHandler = RedirectHandler.create();
+ redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0);
+
+ checkUrl(INTENT_URL_WITH_FALLBACK_URL)
+ .withHasUserGesture(false)
+ .withRedirectHandler(redirectHandler)
+ .withPageTransition(PageTransition.LINK)
+ .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS)
+ .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE);
+ }
+
+ @Test
+ @SmallTest
public void testFallbackUrl_IntentResolutionFailsWithoutPackageName() {
// IMDB app isn't installed.
mDelegate.setCanResolveActivityForExternalSchemes(false);
@@ -1255,6 +1324,37 @@ public class ExternalNavigationHandlerTest {
@Test
@SmallTest
+ public void testIntentWithFileSchemeFiltered() {
+ checkUrl("intent://#Intent;package=com.test.package;scheme=file;end;")
+ .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE);
+ }
+
+ @Test
+ @SmallTest
+ public void testIntentWithNoSchemeLaunched() {
+ checkUrl("intent://#Intent;package=com.test.package;end;")
+ .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
+ }
+
+ @Test
+ @SmallTest
+ public void testIntentWithEmptySchemeLaunched() {
+ checkUrl("intent://#Intent;package=com.test.package;scheme=;end;")
+ .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
+ }
+
+ @Test
+ @SmallTest
+ public void testIntentWithWeirdSchemeLaunched() {
+ checkUrl("intent://#Intent;package=com.test.package;scheme=w3irD;end;")
+ .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
+ }
+
+ @Test
+ @SmallTest
public void testIntentWithMissingReferrer() {
mDelegate.add(new IntentActivity("http://refertest.com", "refertest"));
mDelegate.add(new IntentActivity("https://refertest.com", "refertest"));
@@ -1528,7 +1628,7 @@ public class ExternalNavigationHandlerTest {
.withHasUserGesture(true)
.expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
START_OTHER_ACTIVITY);
- Assert.assertTrue(mDelegate.maybeSetUserGestureCalled);
+ Assert.assertTrue(mDelegate.maybeSetRequestMetadataCalled);
Assert.assertFalse(mDelegate.startIncognitoIntentCalled);
}
@@ -1544,12 +1644,26 @@ public class ExternalNavigationHandlerTest {
.withIsIncognito(true)
.expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_ASYNC_ACTION,
START_INCOGNITO | START_OTHER_ACTIVITY);
- Assert.assertTrue(mDelegate.maybeSetUserGestureCalled);
+ Assert.assertTrue(mDelegate.maybeSetRequestMetadataCalled);
Assert.assertTrue(mDelegate.startIncognitoIntentCalled);
}
@Test
@SmallTest
+ public void testRendererInitiated() {
+ // IMDB app is installed.
+ mDelegate.add(new IntentActivity("imdb:", INTENT_APP_PACKAGE_NAME));
+
+ checkUrl(INTENT_URL_WITH_FALLBACK_URL)
+ .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS)
+ .withIsRendererInitiated(true)
+ .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
+ START_OTHER_ACTIVITY);
+ Assert.assertTrue(mDelegate.maybeSetRequestMetadataCalled);
+ }
+
+ @Test
+ @SmallTest
public void testAutofillAssistantIntentWithFallback_InRegular() {
mDelegate.setIsIntentToAutofillAssistant(true);
checkUrl(AUTOFILL_ASSISTANT_INTENT_URL_WITH_FALLBACK)
@@ -2046,8 +2160,9 @@ public class ExternalNavigationHandlerTest {
}
@Override
- public void maybeSetUserGesture(Intent intent) {
- maybeSetUserGestureCalled = true;
+ public void maybeSetRequestMetadata(Intent intent, boolean hasUserGesture,
+ boolean isRendererInitiated, Origin initiatorOrigin) {
+ maybeSetRequestMetadataCalled = true;
}
@Override
@@ -2172,7 +2287,7 @@ public class ExternalNavigationHandlerTest {
public Intent startActivityIntent;
public boolean startIncognitoIntentCalled;
public boolean handleIncognitoIntentTargetingSelfCalled;
- public boolean maybeSetUserGestureCalled;
+ public boolean maybeSetRequestMetadataCalled;
private String mReferrerWebappPackageName;
@@ -2212,6 +2327,8 @@ public class ExternalNavigationHandlerTest {
private boolean mIsBackgroundTabNavigation;
private boolean mHasUserGesture;
private RedirectHandler mRedirectHandler;
+ private boolean mIsRendererInitiated;
+ private boolean mIsMainFrame = true;
private ExternalNavigationTestParams(String url) {
mUrl = url;
@@ -2259,6 +2376,16 @@ public class ExternalNavigationHandlerTest {
return this;
}
+ public ExternalNavigationTestParams withIsRendererInitiated(boolean isRendererInitiated) {
+ mIsRendererInitiated = isRendererInitiated;
+ return this;
+ }
+
+ public ExternalNavigationTestParams withIsMainFrame(boolean isMainFrame) {
+ mIsMainFrame = isMainFrame;
+ return this;
+ }
+
public void expecting(
@OverrideUrlLoadingResult int expectedOverrideResult, int otherExpectation) {
boolean expectStartIncognito = (otherExpectation & START_INCOGNITO) != 0;
@@ -2279,9 +2406,10 @@ public class ExternalNavigationHandlerTest {
.setApplicationMustBeInForeground(mChromeAppInForegroundRequired)
.setRedirectHandler(mRedirectHandler)
.setIsBackgroundTabNavigation(mIsBackgroundTabNavigation)
- .setIsMainFrame(true)
+ .setIsMainFrame(mIsMainFrame)
.setNativeClientPackageName(mDelegate.getReferrerWebappPackageName())
.setHasUserGesture(mHasUserGesture)
+ .setIsRendererInitiated(mIsRendererInitiated)
.build();
@OverrideUrlLoadingResult
int result = mUrlHandler.shouldOverrideUrlLoading(params);
diff --git a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
index b1c1c111b98..a9d838b7dcf 100644
--- a/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
+++ b/chromium/components/external_intents/android/javatests/src/org/chromium/components/external_intents/RedirectHandlerTest.java
@@ -10,9 +10,10 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.SystemClock;
import android.provider.Browser;
-import android.support.test.filters.SmallTest;
import android.test.mock.MockPackageManager;
+import androidx.test.filters.SmallTest;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -24,7 +25,6 @@ import org.chromium.base.PackageManagerUtils;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.AdvancedMockContext;
import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.RetryOnFailure;
import org.chromium.ui.base.PageTransition;
import java.net.URISyntaxException;
@@ -353,7 +353,6 @@ public class RedirectHandlerTest {
@Test
@SmallTest
@Feature({"IntentHandling"})
- @RetryOnFailure
public void testNavigationFromLinkWithoutUserGesture() {
RedirectHandler handler = RedirectHandler.create();
handler.updateIntent(sYtIntent, false, false, false);
@@ -361,12 +360,11 @@ public class RedirectHandlerTest {
Assert.assertFalse(handler.shouldStayInApp(false));
Assert.assertFalse(handler.shouldStayInApp(true));
- handler.updateNewUrlLoading(
- PageTransition.LINK, false, false, SystemClock.elapsedRealtime(), 0);
+ long lastUserInteractionTime = SystemClock.elapsedRealtime();
+ handler.updateNewUrlLoading(PageTransition.LINK, false, false, lastUserInteractionTime, 0);
Assert.assertTrue(handler.shouldStayInApp(false));
Assert.assertTrue(handler.shouldStayInApp(true));
- handler.updateNewUrlLoading(
- PageTransition.LINK, false, false, SystemClock.elapsedRealtime(), 1);
+ handler.updateNewUrlLoading(PageTransition.LINK, false, false, lastUserInteractionTime, 1);
Assert.assertTrue(handler.shouldStayInApp(false));
Assert.assertTrue(handler.shouldStayInApp(true));
@@ -386,7 +384,6 @@ public class RedirectHandlerTest {
@Test
@SmallTest
@Feature({"IntentHandling"})
- @RetryOnFailure
public void testNavigationFromReload() {
RedirectHandler handler = RedirectHandler.create();
handler.updateIntent(sYtIntent, false, false, false);
@@ -394,12 +391,12 @@ public class RedirectHandlerTest {
Assert.assertFalse(handler.shouldStayInApp(false));
Assert.assertFalse(handler.shouldStayInApp(true));
+ long lastUserInteractionTime = SystemClock.elapsedRealtime();
handler.updateNewUrlLoading(
- PageTransition.RELOAD, false, false, SystemClock.elapsedRealtime(), 0);
+ PageTransition.RELOAD, false, false, lastUserInteractionTime, 0);
Assert.assertTrue(handler.shouldStayInApp(false));
Assert.assertTrue(handler.shouldStayInApp(true));
- handler.updateNewUrlLoading(
- PageTransition.LINK, false, false, SystemClock.elapsedRealtime(), 1);
+ handler.updateNewUrlLoading(PageTransition.LINK, false, false, lastUserInteractionTime, 1);
Assert.assertTrue(handler.shouldStayInApp(false));
Assert.assertTrue(handler.shouldStayInApp(true));
@@ -419,7 +416,6 @@ public class RedirectHandlerTest {
@Test
@SmallTest
@Feature({"IntentHandling"})
- @RetryOnFailure
public void testNavigationWithForwardBack() {
RedirectHandler handler = RedirectHandler.create();
handler.updateIntent(sYtIntent, false, false, false);
@@ -427,12 +423,12 @@ public class RedirectHandlerTest {
Assert.assertFalse(handler.shouldStayInApp(false));
Assert.assertFalse(handler.shouldStayInApp(true));
+ long lastUserInteractionTime = SystemClock.elapsedRealtime();
handler.updateNewUrlLoading(PageTransition.FORM_SUBMIT | PageTransition.FORWARD_BACK, false,
- true, SystemClock.elapsedRealtime(), 0);
+ true, lastUserInteractionTime, 0);
Assert.assertTrue(handler.shouldStayInApp(false));
Assert.assertTrue(handler.shouldStayInApp(true));
- handler.updateNewUrlLoading(
- PageTransition.LINK, false, false, SystemClock.elapsedRealtime(), 1);
+ handler.updateNewUrlLoading(PageTransition.LINK, false, false, lastUserInteractionTime, 1);
Assert.assertTrue(handler.shouldStayInApp(false));
Assert.assertTrue(handler.shouldStayInApp(true));