summaryrefslogtreecommitdiff
path: root/chromium/components/external_intents/android/java
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/java
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/java')
-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
4 files changed, 195 insertions, 65 deletions
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);
}
/**