| // 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.annotation.SuppressLint; |
| import android.content.Context; |
| import android.content.ContextWrapper; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.net.Uri; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.SystemClock; |
| import android.provider.Browser; |
| import android.support.test.InstrumentationRegistry; |
| 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.Test; |
| import org.junit.runner.RunWith; |
| |
| 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.ExternalNavigationHandler.OverrideUrlLoadingResult; |
| import org.chromium.content_public.browser.LoadUrlParams; |
| import org.chromium.content_public.browser.WebContents; |
| 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; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Instrumentation tests for {@link ExternalNavigationHandler}. |
| */ |
| @RunWith(BaseJUnit4ClassRunner.class) |
| // 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 |
| // Expectations |
| private static final int IGNORE = 0x0; |
| private static final int START_INCOGNITO = 0x1; |
| private static final int START_WEBAPK = 0x2; |
| private static final int START_FILE = 0x4; |
| private static final int START_OTHER_ACTIVITY = 0x10; |
| private static final int INTENT_SANITIZATION_EXCEPTION = 0x20; |
| private static final int PROXY_FOR_INSTANT_APPS = 0x40; |
| |
| private static final boolean IS_CUSTOM_TAB_INTENT = true; |
| private static final boolean SEND_TO_EXTERNAL_APPS = true; |
| private static final boolean IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED = true; |
| private static final boolean HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY = true; |
| |
| private static final String SEARCH_RESULT_URL_FOR_TOM_HANKS = |
| "https://www.google.com/search?q=tom+hanks"; |
| private static final String IMDB_WEBPAGE_FOR_TOM_HANKS = "http://m.imdb.com/name/nm0000158"; |
| private static final String INTENT_URL_WITH_FALLBACK_URL = |
| "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;" |
| + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "=" |
| + Uri.encode(IMDB_WEBPAGE_FOR_TOM_HANKS) + ";end"; |
| private static final String INTENT_URL_WITH_FALLBACK_URL_WITHOUT_PACKAGE_NAME = |
| "intent:///name/nm0000158#Intent;scheme=imdb;" |
| + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "=" |
| + Uri.encode(IMDB_WEBPAGE_FOR_TOM_HANKS) + ";end"; |
| private static final String SOME_JAVASCRIPT_PAGE = "javascript:window.open(0);"; |
| private static final String INTENT_URL_WITH_JAVASCRIPT_FALLBACK_URL = |
| "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;" |
| + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "=" |
| + Uri.encode(SOME_JAVASCRIPT_PAGE) + ";end"; |
| private static final String IMDB_APP_INTENT_FOR_TOM_HANKS = "imdb:///name/nm0000158"; |
| private static final String INTENT_URL_WITH_CHAIN_FALLBACK_URL = |
| "intent://scan/#Intent;scheme=zxing;" |
| + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "=" |
| + Uri.encode("http://url.myredirector.com/aaa") + ";end"; |
| private static final String ENCODED_MARKET_REFERRER = |
| "_placement%3D{placement}%26network%3D{network}%26device%3D{devicemodel}"; |
| private static final String INTENT_APP_NOT_INSTALLED_DEFAULT_MARKET_REFERRER = |
| "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;end"; |
| private static final String INTENT_APP_NOT_INSTALLED_WITH_MARKET_REFERRER = |
| "intent:///name/nm0000158#Intent;scheme=imdb;package=com.imdb.mobile;S." |
| + ExternalNavigationHandler.EXTRA_MARKET_REFERRER + "=" + ENCODED_MARKET_REFERRER |
| + ";end"; |
| private static final String INTENT_URL_FOR_CHROME_CUSTOM_TABS = "intent://example.com#Intent;" |
| + "package=org.chromium.chrome;" |
| + "action=android.intent.action.VIEW;" |
| + "scheme=http;" |
| + "S.android.support.customtabs.extra.SESSION=;" |
| + "end;"; |
| private static final String INTENT_URL_FOR_CHROME = "intent://example.com#Intent;" |
| + "package=org.chromium.chrome;" |
| + "action=android.intent.action.VIEW;" |
| + "scheme=http;" |
| + "end;"; |
| private static final String INTENT_APP_PACKAGE_NAME = "com.imdb.mobile"; |
| private static final String YOUTUBE_URL = "http://youtube.com"; |
| private static final String YOUTUBE_MOBILE_URL = "http://m.youtube.com"; |
| private static final String YOUTUBE_PACKAGE_NAME = "youtube"; |
| |
| private static final String PLUS_STREAM_URL = "https://plus.google.com/stream"; |
| private static final String CALENDAR_URL = "http://www.google.com/calendar"; |
| private static final String KEEP_URL = "http://www.google.com/keep"; |
| |
| private static final String TEXT_APP_1_PACKAGE_NAME = "text_app_1"; |
| private static final String TEXT_APP_2_PACKAGE_NAME = "text_app_2"; |
| |
| private static final String WEBAPK_SCOPE = "https://www.template.com"; |
| private static final String WEBAPK_PACKAGE_PREFIX = "org.chromium.webapk"; |
| private static final String WEBAPK_PACKAGE_NAME = WEBAPK_PACKAGE_PREFIX + ".template"; |
| private static final String INVALID_WEBAPK_PACKAGE_NAME = WEBAPK_PACKAGE_PREFIX + ".invalid"; |
| |
| private static final String[] SUPERVISOR_START_ACTIONS = { |
| "com.google.android.instantapps.START", "com.google.android.instantapps.nmr1.INSTALL", |
| "com.google.android.instantapps.nmr1.VIEW"}; |
| |
| private static final String AUTOFILL_ASSISTANT_INTENT_URL_WITH_FALLBACK = |
| "intent://www.example.com#Intent;scheme=https;" |
| + "B.org.chromium.chrome.browser.autofill_assistant.ENABLED=true;" |
| + "S." + ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL + "=" |
| + Uri.encode("https://www.example.com") + ";end"; |
| |
| private static final String AUTOFILL_ASSISTANT_INTENT_URL_WITHOUT_FALLBACK = |
| "intent://www.example.com#Intent;scheme=https;" |
| + "B.org.chromium.chrome.browser.autofill_assistant.ENABLED=true;" |
| + "end;"; |
| |
| private static final String IS_INSTANT_APP_EXTRA = "IS_INSTANT_APP"; |
| |
| private Context mContext; |
| private final TestExternalNavigationDelegate mDelegate; |
| private ExternalNavigationHandlerForTesting mUrlHandler; |
| |
| public ExternalNavigationHandlerTest() { |
| mDelegate = new TestExternalNavigationDelegate(); |
| mUrlHandler = new ExternalNavigationHandlerForTesting(mDelegate); |
| } |
| |
| @Before |
| public void setUp() { |
| mContext = new TestContext(InstrumentationRegistry.getTargetContext(), mDelegate); |
| ContextUtils.initApplicationContextForTests(mContext); |
| |
| NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess(); |
| } |
| |
| @Test |
| @SmallTest |
| public void testStartActivityToTrustedPackageWithoutUserGesture() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler handler = RedirectHandler.create(); |
| handler.updateNewUrlLoading(PageTransition.CLIENT_REDIRECT, false, false, 0, 0); |
| |
| checkUrl(YOUTUBE_URL) |
| .withRedirectHandler(handler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| mDelegate.setIsCallingAppTrusted(true); |
| |
| checkUrl(YOUTUBE_URL) |
| .withRedirectHandler(handler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testOrdinaryIncognitoUri() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| // http://crbug.com/587306: Don't prompt the user for capturing URLs in incognito, just keep |
| // it within the browser. |
| checkUrl(YOUTUBE_URL) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testChromeReferrer() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| // http://crbug.com/159153: Don't override http or https URLs from the NTP or bookmarks. |
| checkUrl(YOUTUBE_URL) |
| .withReferrer("chrome://about") |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| checkUrl("tel:012345678") |
| .withReferrer("chrome://about") |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testForwardBackNavigation() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| // http://crbug.com/164194. We shouldn't show the intent picker on |
| // forwards or backwards navigations. |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(PageTransition.LINK | PageTransition.FORWARD_BACK) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testRedirectFromFormSubmit() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| // http://crbug.com/181186: We need to show the intent picker when we receive a redirect |
| // following a form submit. OAuth of native applications rely on this. |
| checkUrl("market://1234") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| checkUrl("http://youtube.com://") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| // If the page matches the referrer, then continue loading in Chrome. |
| checkUrl("http://youtube.com://") |
| .withReferrer(YOUTUBE_URL) |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(true) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // If the page does not match the referrer, then prompt an intent. |
| checkUrl("http://youtube.com://") |
| .withReferrer("http://google.com") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| // It doesn't make sense to allow intent picker without redirect, since form data |
| // is not encoded in the intent (although, in theory, it could be passed in as |
| // an extra data in the intent). |
| checkUrl("http://youtube.com://") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withHasUserGesture(true) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testRedirectFromFormSubmit_NoUserGesture() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| // If the redirect is not associated with a user gesture, then continue loading in Chrome. |
| checkUrl("market://1234") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(false) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| checkUrl("http://youtube.com://") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(false) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testRedirectFromFormSubmit_NoUserGesture_OnIntentRedirectChain() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = new RedirectHandler() { |
| @Override |
| public boolean isOnEffectiveIntentRedirectChain() { |
| return true; |
| } |
| }; |
| |
| // If the redirect is not associated with a user gesture but came from an incoming intent, |
| // then allow those to launch external intents. |
| checkUrl("market://1234") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(false) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| checkUrl("http://youtube.com://") |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(false) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testOrdinary_disableExternalIntentRequestsForUrl() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| mDelegate.setDisableExternalIntentRequests(true); |
| |
| checkUrl(YOUTUBE_URL).expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIgnore() { |
| // Ensure the following URLs are not broadcast for external navigation. |
| String urlsToIgnore[] = new String[] {"about:test", |
| "content:test", // Content URLs should not be exposed outside of Chrome. |
| "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( |
| OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testPageTransitionType() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| // Non-link page transition type are ignored. |
| checkUrl(YOUTUBE_URL) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| checkUrl(YOUTUBE_URL) |
| .withIsRedirect(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| // http://crbug.com/143118 - Don't show the picker for directly typed URLs, unless |
| // the URL results in a redirect. |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(PageTransition.TYPED) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // http://crbug.com/162106 - Don't show the picker on reload. |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(PageTransition.RELOAD) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testWtai() { |
| // Start the telephone application with the given number. |
| checkUrl("wtai://wp/mc;0123456789") |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY | INTENT_SANITIZATION_EXCEPTION); |
| |
| // These two cases are currently unimplemented. |
| checkUrl("wtai://wp/sd;0123456789") |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, |
| IGNORE | INTENT_SANITIZATION_EXCEPTION); |
| checkUrl("wtai://wp/ap;0123456789") |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, |
| IGNORE | INTENT_SANITIZATION_EXCEPTION); |
| |
| // Ignore other WTAI urls. |
| checkUrl("wtai://wp/invalid") |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, |
| IGNORE | INTENT_SANITIZATION_EXCEPTION); |
| } |
| |
| @Test |
| @SmallTest |
| public void testRedirectToMarketWithReferrer() { |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| checkUrl(INTENT_APP_NOT_INSTALLED_WITH_MARKET_REFERRER) |
| .withReferrer(KEEP_URL) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertNotNull(mDelegate.startActivityIntent); |
| Uri uri = mDelegate.startActivityIntent.getData(); |
| Assert.assertEquals("market", uri.getScheme()); |
| Assert.assertEquals(Uri.decode(ENCODED_MARKET_REFERRER), uri.getQueryParameter("referrer")); |
| Assert.assertEquals(Uri.parse(KEEP_URL), |
| mDelegate.startActivityIntent.getParcelableExtra(Intent.EXTRA_REFERRER)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testRedirectToMarketWithoutReferrer() { |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| checkUrl(INTENT_APP_NOT_INSTALLED_DEFAULT_MARKET_REFERRER) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertNotNull(mDelegate.startActivityIntent); |
| Uri uri = mDelegate.startActivityIntent.getData(); |
| Assert.assertEquals("market", uri.getScheme()); |
| Assert.assertEquals(getPackageName(), uri.getQueryParameter("referrer")); |
| } |
| |
| @Test |
| @SmallTest |
| public void testExternalUri() { |
| checkUrl("tel:012345678") |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testTypedRedirectToExternalProtocol() { |
| // http://crbug.com/169549 |
| checkUrl("market://1234") |
| .withPageTransition(PageTransition.TYPED) |
| .withIsRedirect(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| // http://crbug.com/709217 |
| checkUrl("market://1234") |
| .withPageTransition(PageTransition.FROM_ADDRESS_BAR) |
| .withIsRedirect(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| // http://crbug.com/143118 |
| checkUrl("market://1234") |
| .withPageTransition(PageTransition.TYPED) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIncomingIntentRedirect() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| int transitionTypeIncomingIntent = PageTransition.LINK | PageTransition.FROM_API; |
| // http://crbug.com/149218 |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(transitionTypeIncomingIntent) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // http://crbug.com/170925 |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(transitionTypeIncomingIntent) |
| .withIsRedirect(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIntentScheme() { |
| String url = "intent:wtai://wp/#Intent;action=android.settings.SETTINGS;" |
| + "component=package/class;end"; |
| String urlWithSel = "intent:wtai://wp/#Intent;SEL;action=android.settings.SETTINGS;" |
| + "component=package/class;end"; |
| String urlWithNullData = "intent:#Intent;package=com.google.zxing.client.android;" |
| + "action=android.settings.SETTINGS;end"; |
| |
| checkUrl(url).expecting( |
| OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_OTHER_ACTIVITY); |
| |
| // http://crbug.com/370399 |
| checkUrl(urlWithSel) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| checkUrl(urlWithNullData) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testYouTubePairingCode() { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| int transitionTypeIncomingIntent = PageTransition.LINK | PageTransition.FROM_API; |
| final String[] goodUrls = {"http://m.youtube.com/watch?v=1234&pairingCode=5678", |
| "youtube.com?pairingCode=xyz", "youtube.com/tv?pairingCode=xyz", |
| "youtube.com/watch?v=1234&version=3&autohide=1&pairingCode=xyz", |
| "youtube.com/watch?v=1234&pairingCode=xyz&version=3&autohide=1"}; |
| final String[] badUrls = {"youtube.com.foo.com/tv?pairingCode=xyz", |
| "youtube.com.foo.com?pairingCode=xyz", |
| "youtube.com/watch?v=tEsT&version=3&autohide=1&pairingCode=", |
| "youtube.com&pairingCode=xyz", |
| "youtube.com/watch?v=tEsT?version=3&pairingCode=&autohide=1"}; |
| |
| // Make sure we don't override when faced with valid pairing code URLs. |
| for (String url : goodUrls) { |
| // http://crbug/386600 - it makes no sense to switch activities for pairing code URLs. |
| checkUrl(url).withIsRedirect(true).expecting( |
| OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| checkUrl(url) |
| .withPageTransition(transitionTypeIncomingIntent) |
| .withIsRedirect(true) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| // The pairing code URL regex shouldn't cause NO_OVERRIDE on invalid URLs. |
| for (String url : badUrls) { |
| checkUrl(url).withIsRedirect(true).expecting( |
| OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_OTHER_ACTIVITY); |
| |
| checkUrl(url) |
| .withPageTransition(transitionTypeIncomingIntent) |
| .withIsRedirect(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| } |
| |
| @Test |
| @SmallTest |
| public void testInitialIntent() throws URISyntaxException { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| Intent ytIntent = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME); |
| Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME); |
| int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; |
| |
| // Ignore if url is redirected, transition type is IncomingIntent and a new intent doesn't |
| // have any new resolver. |
| redirectHandler.updateIntent(ytIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // Do not ignore if a new intent has any new resolver. |
| redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| // Do not ignore if a new intent cannot be handled by Chrome. |
| redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| checkUrl("intent://myownurl") |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testInitialIntentHeadingToChrome() throws URISyntaxException { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME); |
| fooIntent.setPackage(mContext.getPackageName()); |
| int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; |
| |
| // Ignore if an initial Intent was heading to Chrome. |
| redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // Do not ignore if the URI has an external protocol. |
| redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| checkUrl("market://1234") |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIntentForCustomTab() throws URISyntaxException { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; |
| |
| // In Custom Tabs, if the first url is not a redirect, stay in chrome. |
| Intent barIntent = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME); |
| barIntent.setPackage(mContext.getPackageName()); |
| redirectHandler.updateIntent(barIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(transTypeLinkFromIntent) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // In Custom Tabs, if the first url is a redirect, don't allow it to intent out. |
| Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME); |
| fooIntent.setPackage(mContext.getPackageName()); |
| redirectHandler.updateIntent(fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // In Custom Tabs, if the external handler extra is present, intent out if the first |
| // url is a redirect. |
| Intent extraIntent2 = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME); |
| extraIntent2.setPackage(mContext.getPackageName()); |
| redirectHandler.updateIntent(extraIntent2, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Intent extraIntent3 = Intent.parseUri(YOUTUBE_URL, Intent.URI_INTENT_SCHEME); |
| extraIntent3.setPackage(mContext.getPackageName()); |
| redirectHandler.updateIntent(extraIntent3, IS_CUSTOM_TAB_INTENT, SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| // External intent for a user-initiated navigation should always be allowed. |
| redirectHandler.updateIntent(fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| // Simulate a real user navigation. |
| redirectHandler.updateNewUrlLoading( |
| PageTransition.LINK, false, true, SystemClock.elapsedRealtime() + 1, 0); |
| checkUrl(YOUTUBE_URL) |
| .withPageTransition(PageTransition.LINK) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testInstantAppsIntent_customTabRedirect() throws Exception { |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; |
| |
| // In Custom Tabs, if the first url is a redirect, don't allow it to intent out, unless |
| // the redirect is going to Instant Apps. |
| Intent fooIntent = Intent.parseUri("http://foo.com/", Intent.URI_INTENT_SCHEME); |
| fooIntent.putExtra(CustomTabsIntent.EXTRA_ENABLE_INSTANT_APPS, true); |
| fooIntent.setPackage(mContext.getPackageName()); |
| redirectHandler.updateIntent(fooIntent, IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| |
| mDelegate.setCanHandleWithInstantApp(true); |
| checkUrl("http://instantappenabled.com") |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testCCTIntentUriDoesNotFireCCTAndLoadInChrome_InIncognito() throws Exception { |
| mDelegate.setCanLoadUrlInTab(false); |
| checkUrl(INTENT_URL_FOR_CHROME_CUSTOM_TABS) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, IGNORE); |
| Assert.assertTrue(mDelegate.handleIncognitoIntentTargetingSelfCalled); |
| } |
| |
| @Test |
| @SmallTest |
| public void testCCTIntentUriFiresCCT_InRegular() throws Exception { |
| checkUrl(INTENT_URL_FOR_CHROME_CUSTOM_TABS) |
| .withIsIncognito(false) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| Assert.assertFalse(mDelegate.handleIncognitoIntentTargetingSelfCalled); |
| } |
| |
| @Test |
| @SmallTest |
| public void testChromeIntentUriDoesNotFireAndLoadsInChrome_InIncognito() throws Exception { |
| mDelegate.setCanLoadUrlInTab(false); |
| checkUrl(INTENT_URL_FOR_CHROME) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, IGNORE); |
| Assert.assertTrue(mDelegate.handleIncognitoIntentTargetingSelfCalled); |
| } |
| |
| @Test |
| @SmallTest |
| public void testInstantAppsIntent_incomingIntentRedirect() throws Exception { |
| int transTypeLinkFromIntent = PageTransition.LINK | PageTransition.FROM_API; |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| Intent fooIntent = |
| Intent.parseUri("http://instantappenabled.com", Intent.URI_INTENT_SCHEME); |
| redirectHandler.updateIntent(fooIntent, !IS_CUSTOM_TAB_INTENT, !SEND_TO_EXTERNAL_APPS, |
| IS_CCT_EXTERNAL_LINK_HANDLING_ENABLED); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, false, false, 0, 0); |
| redirectHandler.updateNewUrlLoading(transTypeLinkFromIntent, true, false, 0, 0); |
| |
| mDelegate.setCanHandleWithInstantApp(true); |
| checkUrl("http://goo.gl/1234") |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, IGNORE); |
| |
| // URL that cannot be handled with instant apps should stay in Chrome. |
| mDelegate.setCanHandleWithInstantApp(false); |
| checkUrl("http://goo.gl/1234") |
| .withPageTransition(transTypeLinkFromIntent) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testInstantAppsIntent_handleNavigation() { |
| mDelegate.setCanHandleWithInstantApp(false); |
| checkUrl("http://maybeinstantapp.com") |
| .withPageTransition(PageTransition.LINK) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| mDelegate.setCanHandleWithInstantApp(true); |
| checkUrl("http://maybeinstantapp.com") |
| .withPageTransition(PageTransition.LINK) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testHandlingOfInstantApps() { |
| String intentUrl = "intent://buzzfeed.com/tasty#Intent;scheme=http;" |
| + "package=com.google.android.instantapps.supervisor;" |
| + "action=com.google.android.instantapps.START;" |
| + "S.com.google.android.instantapps.FALLBACK_PACKAGE=" |
| + "com.android.chrome;S.com.google.android.instantapps.INSTANT_APP_PACKAGE=" |
| + "com.yelp.android;S.android.intent.extra.REFERRER_NAME=" |
| + "https%3A%2F%2Fwww.google.com;end"; |
| |
| mUrlHandler.mIsSerpReferrer = true; |
| mDelegate.setIsIntentToInstantApp(true); |
| checkUrl(intentUrl).expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY | PROXY_FOR_INSTANT_APPS); |
| Assert.assertTrue( |
| mDelegate.startActivityIntent.getBooleanExtra(IS_INSTANT_APP_EXTRA, false)); |
| |
| // Check that we block all instant app intent:// URLs not from SERP. |
| mUrlHandler.mIsSerpReferrer = false; |
| checkUrl(intentUrl).expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // Check that that just having the SERP referrer alone doesn't cause intents to be treated |
| // as intents to instant apps if the delegate indicates that they shouldn't be. |
| mUrlHandler.mIsSerpReferrer = true; |
| mDelegate.setIsIntentToInstantApp(false); |
| checkUrl(intentUrl).expecting( |
| OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_OTHER_ACTIVITY); |
| Assert.assertFalse( |
| mDelegate.startActivityIntent.getBooleanExtra(IS_INSTANT_APP_EXTRA, true)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_IntentResolutionSucceeds() { |
| // 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) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Intent invokedIntent = mDelegate.startActivityIntent; |
| Assert.assertEquals(IMDB_APP_INTENT_FOR_TOM_HANKS, invokedIntent.getData().toString()); |
| Assert.assertNull("The invoked intent should not have browser_fallback_url\n", |
| invokedIntent.getStringExtra(ExternalNavigationHandler.EXTRA_BROWSER_FALLBACK_URL)); |
| Assert.assertNull(mUrlHandler.mNewUrlAfterClobbering); |
| Assert.assertNull(mUrlHandler.mReferrerUrlForClobbering); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_IntentResolutionSucceedsInIncognito() { |
| // IMDB app is installed. |
| mDelegate.add(new IntentActivity("imdb:", INTENT_APP_PACKAGE_NAME)); |
| |
| // Expect that the user is prompted to leave incognito mode. |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL) |
| .withIsIncognito(true) |
| .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_ASYNC_ACTION, |
| START_INCOGNITO | START_OTHER_ACTIVITY); |
| |
| Assert.assertNull(mUrlHandler.mNewUrlAfterClobbering); |
| Assert.assertNull(mUrlHandler.mReferrerUrlForClobbering); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_FallbackToWebApk() { |
| // IMDB app isn't installed. |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| mDelegate.add(new IntentActivity(IMDB_WEBPAGE_FOR_TOM_HANKS, WEBAPK_PACKAGE_NAME)); |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL) |
| .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_WEBAPK); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_DontFallbackToWebApkMultipleHandlers() { |
| // IMDB app isn't installed. |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| mDelegate.add(new IntentActivity(IMDB_WEBPAGE_FOR_TOM_HANKS, WEBAPK_PACKAGE_NAME)); |
| mDelegate.add(new IntentActivity(IMDB_WEBPAGE_FOR_TOM_HANKS, TEXT_APP_1_PACKAGE_NAME)); |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL) |
| .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| Assert.assertNull(mDelegate.startActivityIntent); |
| Assert.assertEquals(IMDB_WEBPAGE_FOR_TOM_HANKS, mUrlHandler.mNewUrlAfterClobbering); |
| Assert.assertEquals(SEARCH_RESULT_URL_FOR_TOM_HANKS, mUrlHandler.mReferrerUrlForClobbering); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_IntentResolutionFails() { |
| // IMDB app isn't installed. |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| // When intent resolution fails, we should not start an activity, but instead clobber |
| // the current tab. |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL) |
| .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| Assert.assertEquals(IMDB_WEBPAGE_FOR_TOM_HANKS, mUrlHandler.mNewUrlAfterClobbering); |
| Assert.assertEquals(SEARCH_RESULT_URL_FOR_TOM_HANKS, mUrlHandler.mReferrerUrlForClobbering); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_FallbackToMarketApp() { |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| 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).expecting( |
| OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_OTHER_ACTIVITY); |
| |
| Assert.assertEquals("market://details?id=com.imdb.mobile&referrer=mypage", |
| mDelegate.startActivityIntent.getDataString()); |
| |
| String intentNoRef = "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;end"; |
| checkUrl(intentNoRef) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertEquals("market://details?id=com.imdb.mobile&referrer=" + getPackageName(), |
| mDelegate.startActivityIntent.getDataString()); |
| |
| 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) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| } |
| |
| @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.LINK, false, true, 0, 0); |
| checkUrl("http://goo.gl/abcdefg") |
| .withPageTransition(PageTransition.LINK) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| 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" |
| + "&referrer=mypage;end"; |
| |
| checkUrl(realIntent) |
| .withPageTransition(PageTransition.LINK) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertEquals("market://details?id=com.imdb.mobile&referrer=mypage", |
| mDelegate.startActivityIntent.getDataString()); |
| } |
| |
| @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)); |
| |
| 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)); |
| |
| 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); |
| |
| // Fallback URL should work even when package name isn't given. |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL_WITHOUT_PACKAGE_NAME) |
| .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| Assert.assertEquals(IMDB_WEBPAGE_FOR_TOM_HANKS, mUrlHandler.mNewUrlAfterClobbering); |
| Assert.assertEquals(SEARCH_RESULT_URL_FOR_TOM_HANKS, mUrlHandler.mReferrerUrlForClobbering); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_FallbackShouldNotWarnOnIncognito() { |
| // IMDB app isn't installed. |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL) |
| .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| Assert.assertEquals(IMDB_WEBPAGE_FOR_TOM_HANKS, mUrlHandler.mNewUrlAfterClobbering); |
| Assert.assertEquals(SEARCH_RESULT_URL_FOR_TOM_HANKS, mUrlHandler.mReferrerUrlForClobbering); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallbackUrl_IgnoreJavascriptFallbackUrl() { |
| // IMDB app isn't installed. |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| // Will be redirected to market since package is given. |
| checkUrl(INTENT_URL_WITH_JAVASCRIPT_FALLBACK_URL) |
| .withReferrer(SEARCH_RESULT_URL_FOR_TOM_HANKS) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_ASYNC_ACTION, |
| START_INCOGNITO | START_OTHER_ACTIVITY); |
| |
| Intent invokedIntent = mUrlHandler.mStartActivityInIncognitoIntent; |
| Assert.assertTrue(invokedIntent.getData().toString().startsWith("market://")); |
| Assert.assertEquals(null, mUrlHandler.mNewUrlAfterClobbering); |
| Assert.assertEquals(null, mUrlHandler.mReferrerUrlForClobbering); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFallback_UseFallbackUrlForRedirectionFromTypedInUrl() { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0); |
| checkUrl("http://goo.gl/abcdefg") |
| .withPageTransition(PageTransition.TYPED) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0); |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL_WITHOUT_PACKAGE_NAME) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| |
| // Now the user opens a link. |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 1); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIgnoreEffectiveRedirectFromIntentFallbackUrl() { |
| // We cannot resolve any intent, so fall-back URL will be used. |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, true, 0, 0); |
| checkUrl(INTENT_URL_WITH_CHAIN_FALLBACK_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| |
| // As a result of intent resolution fallback, we have clobberred the current tab. |
| // The fall-back URL was HTTP-schemed, but it was effectively redirected to a new intent |
| // URL using javascript. However, we do not allow chained fallback intent, so we do NOT |
| // override URL loading here. |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 0); |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| // Now enough time (2 seconds) have passed. |
| // New URL loading should not be affected. |
| // (The URL happened to be the same as previous one.) |
| // TODO(changwan): this is not likely cause flakiness, but it may be better to refactor |
| // systemclock or pass the new time as parameter. |
| long lastUserInteractionTimeInMillis = SystemClock.elapsedRealtime() + 2 * 1000L; |
| redirectHandler.updateNewUrlLoading( |
| PageTransition.LINK, false, true, lastUserInteractionTimeInMillis, 1); |
| checkUrl(INTENT_URL_WITH_FALLBACK_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIgnoreEffectiveRedirectFromUserTyping() { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.TYPED, false, false, 0, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withPageTransition(PageTransition.TYPED) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.TYPED, true, false, 0, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withPageTransition(PageTransition.TYPED) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 0, 1); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testNavigationFromLinkWithoutUserGesture() { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testChromeAppInBackground() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| mDelegate.setIsChromeAppInForeground(false); |
| checkUrl(YOUTUBE_URL).expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testNotChromeAppInForegroundRequired() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| mDelegate.setIsChromeAppInForeground(false); |
| checkUrl(YOUTUBE_URL) |
| .withChromeAppInForegroundRequired(false) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testCreatesIntentsToOpenInNewTab() { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| mUrlHandler = new ExternalNavigationHandlerForTesting(mDelegate); |
| ExternalNavigationParams params = |
| new ExternalNavigationParams.Builder(YOUTUBE_MOBILE_URL, false) |
| .setOpenInNewTab(true) |
| .build(); |
| @OverrideUrlLoadingResult |
| int result = mUrlHandler.shouldOverrideUrlLoading(params); |
| Assert.assertEquals(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, result); |
| Assert.assertTrue(mDelegate.startActivityIntent != null); |
| Assert.assertTrue( |
| mDelegate.startActivityIntent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testCanExternalAppHandleUrl() { |
| mDelegate.setCanResolveActivityForExternalSchemes(false); |
| mDelegate.add(new IntentActivity("some_app", "someapp")); |
| |
| Assert.assertTrue(mUrlHandler.canExternalAppHandleUrl("some_app://some_app.com/")); |
| |
| Assert.assertTrue(mUrlHandler.canExternalAppHandleUrl("wtai://wp/mc;0123456789")); |
| Assert.assertTrue(mUrlHandler.canExternalAppHandleUrl( |
| "intent:/#Intent;scheme=no_app;package=com.no_app;end")); |
| Assert.assertFalse(mUrlHandler.canExternalAppHandleUrl("no_app://no_app.com/")); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPlusAppRefresh() { |
| mDelegate.add(new IntentActivity(PLUS_STREAM_URL, "plus")); |
| |
| checkUrl(PLUS_STREAM_URL) |
| .withReferrer(PLUS_STREAM_URL) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testSameDomainDifferentApps() { |
| mDelegate.add(new IntentActivity(CALENDAR_URL, "calendar")); |
| |
| checkUrl(CALENDAR_URL) |
| .withReferrer(KEEP_URL) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testFormSubmitSameDomain() { |
| mDelegate.add(new IntentActivity(CALENDAR_URL, "calendar")); |
| |
| checkUrl(CALENDAR_URL) |
| .withReferrer(KEEP_URL) |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testBackgroundTabNavigation() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| checkUrl(YOUTUBE_URL) |
| .withIsBackgroundTabNavigation(true) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testPdfDownloadHappensInChrome() { |
| mDelegate.add(new IntentActivity(CALENDAR_URL, "calendar")); |
| |
| checkUrl(CALENDAR_URL + "/file.pdf") |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIntentToPdfFileOpensApp() { |
| checkUrl("intent://yoursite.com/mypdf.pdf#Intent;action=VIEW;category=BROWSABLE;" |
| + "scheme=http;package=com.adobe.reader;end;") |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUsafeIntentFlagsFiltered() { |
| checkUrl("intent://#Intent;package=com.test.package;launchFlags=0x7FFFFFFF;end;") |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| Assert.assertEquals(ExternalNavigationHandler.ALLOWED_INTENT_FLAGS, |
| mDelegate.startActivityIntent.getFlags()); |
| } |
| |
| @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")); |
| |
| // http://crbug.com/702089: Don't override links within the same host/domain. |
| // This is an issue for HTTPS->HTTP because there's no referrer, so we fall back on the |
| // WebContents.lastCommittedUrl. |
| |
| checkUrl("http://refertest.com") |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| mUrlHandler.mLastCommittedUrl = "https://refertest.com"; |
| checkUrl("http://refertest.com").expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testReferrerExtra() { |
| mDelegate.add(new IntentActivity(YOUTUBE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| String referrer = "http://www.google.com"; |
| checkUrl("http://youtube.com:90/foo/bar") |
| .withReferrer(referrer) |
| .withPageTransition(PageTransition.FORM_SUBMIT) |
| .withIsRedirect(true) |
| .withHasUserGesture(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| Assert.assertEquals(Uri.parse(referrer), |
| mDelegate.startActivityIntent.getParcelableExtra(Intent.EXTRA_REFERRER)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testNavigationFromReload() { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.RELOAD, false, false, 1, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testNavigationWithForwardBack() { |
| mDelegate.add(new IntentActivity(YOUTUBE_MOBILE_URL, YOUTUBE_PACKAGE_NAME)); |
| |
| RedirectHandler redirectHandler = RedirectHandler.create(); |
| |
| redirectHandler.updateNewUrlLoading( |
| PageTransition.FORM_SUBMIT | PageTransition.FORWARD_BACK, false, false, 1, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, true, false, 1, 0); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withIsRedirect(true) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| redirectHandler.updateNewUrlLoading(PageTransition.LINK, false, false, 1, 1); |
| checkUrl(YOUTUBE_MOBILE_URL) |
| .withRedirectHandler(redirectHandler) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| } |
| |
| @Test |
| @SuppressLint("SdCardPath") |
| @SmallTest |
| public void testFileAccess() { |
| String fileUrl = "file:///sdcard/Downloads/test.html"; |
| |
| mUrlHandler.mShouldRequestFileAccess = false; |
| // Verify no overrides if file access is allowed (under different load conditions). |
| checkUrl(fileUrl).expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| checkUrl(fileUrl) |
| .withPageTransition(PageTransition.RELOAD) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| checkUrl(fileUrl) |
| .withPageTransition(PageTransition.AUTO_TOPLEVEL) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| mUrlHandler.mShouldRequestFileAccess = true; |
| // Verify that the file intent action is triggered if file access is not allowed. |
| checkUrl(fileUrl) |
| .withPageTransition(PageTransition.AUTO_TOPLEVEL) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_ASYNC_ACTION, START_FILE); |
| } |
| |
| @Test |
| @SmallTest |
| public void testSms_DispatchIntentToDefaultSmsApp() { |
| final String referer = "https://www.google.com/"; |
| mDelegate.add(new IntentActivity("sms", TEXT_APP_1_PACKAGE_NAME)); |
| mDelegate.add(new IntentActivity("sms", TEXT_APP_2_PACKAGE_NAME)); |
| mUrlHandler.defaultSmsPackageName = TEXT_APP_2_PACKAGE_NAME; |
| |
| checkUrl("sms:+012345678?body=hello%20there") |
| .withReferrer(referer) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertNotNull(mDelegate.startActivityIntent); |
| Assert.assertEquals(TEXT_APP_2_PACKAGE_NAME, mDelegate.startActivityIntent.getPackage()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testSms_DefaultSmsAppDoesNotHandleIntent() { |
| final String referer = "https://www.google.com/"; |
| mDelegate.add(new IntentActivity("sms", TEXT_APP_1_PACKAGE_NAME)); |
| mDelegate.add(new IntentActivity("sms", TEXT_APP_2_PACKAGE_NAME)); |
| // Note that this package does not resolve the intent. |
| mUrlHandler.defaultSmsPackageName = "text_app_3"; |
| |
| checkUrl("sms:+012345678?body=hello%20there") |
| .withReferrer(referer) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertNotNull(mDelegate.startActivityIntent); |
| Assert.assertNull(mDelegate.startActivityIntent.getPackage()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testSms_DispatchIntentSchemedUrlToDefaultSmsApp() { |
| final String referer = "https://www.google.com/"; |
| mDelegate.add(new IntentActivity("sms", TEXT_APP_1_PACKAGE_NAME)); |
| mDelegate.add(new IntentActivity("sms", TEXT_APP_2_PACKAGE_NAME)); |
| mUrlHandler.defaultSmsPackageName = TEXT_APP_2_PACKAGE_NAME; |
| |
| checkUrl("intent://012345678?body=hello%20there/#Intent;scheme=sms;end") |
| .withReferrer(referer) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertNotNull(mDelegate.startActivityIntent); |
| Assert.assertEquals(TEXT_APP_2_PACKAGE_NAME, mDelegate.startActivityIntent.getPackage()); |
| } |
| |
| /** |
| * Test that tapping a link which falls solely in the scope of a WebAPK launches a WebAPK |
| * without showing the intent picker. |
| */ |
| @Test |
| @SmallTest |
| public void testLaunchWebApk_BypassIntentPicker() { |
| mDelegate.add(new IntentActivity(WEBAPK_SCOPE, WEBAPK_PACKAGE_NAME)); |
| |
| checkUrl(WEBAPK_SCOPE) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_WEBAPK); |
| } |
| |
| /** |
| * Test that tapping a link which falls in the scope of multiple intent handlers, one of which |
| * is a WebAPK, shows the intent picker. |
| */ |
| @Test |
| @SmallTest |
| public void testLaunchWebApk_ShowIntentPickerMultipleIntentHandlers() { |
| final String scope = "https://www.webapk.with.native.com"; |
| mDelegate.add(new IntentActivity(scope, WEBAPK_PACKAGE_PREFIX + ".with.native")); |
| mDelegate.add(new IntentActivity(scope, "com.webapk.with.native.android")); |
| |
| checkUrl(scope).expecting( |
| OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_OTHER_ACTIVITY); |
| } |
| |
| /** |
| * Test that tapping a link which falls solely into the scope of a different WebAPK launches a |
| * WebAPK without showing the intent picker. |
| */ |
| @Test |
| @SmallTest |
| public void testLaunchWebApk_BypassIntentPickerFromAnotherWebApk() { |
| final String scope1 = "https://www.webapk.with.native.com"; |
| final String scope1WebApkPackageName = WEBAPK_PACKAGE_PREFIX + ".with.native"; |
| final String scope1NativeAppPackageName = "com.webapk.with.native.android"; |
| final String scope2 = "https://www.template.com"; |
| mDelegate.add(new IntentActivity(scope1, scope1WebApkPackageName)); |
| mDelegate.add(new IntentActivity(scope1, scope1NativeAppPackageName)); |
| mDelegate.add(new IntentActivity(scope2, WEBAPK_PACKAGE_NAME)); |
| mDelegate.setReferrerWebappPackageName(scope1WebApkPackageName); |
| |
| checkUrl(scope2).withReferrer(scope1).expecting( |
| OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_WEBAPK); |
| } |
| |
| /** |
| * Test that a link which falls into the scope of an invalid WebAPK (e.g. it was incorrectly |
| * signed) does not get any special WebAPK handling. The first time that the user taps on the |
| * link, the intent picker should be shown. |
| */ |
| @Test |
| @SmallTest |
| public void testLaunchWebApk_ShowIntentPickerInvalidWebApk() { |
| mDelegate.add(new IntentActivity(WEBAPK_SCOPE, INVALID_WEBAPK_PACKAGE_NAME)); |
| checkUrl(WEBAPK_SCOPE) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| } |
| |
| @Test |
| @SmallTest |
| public void testMarketIntent_MarketInstalled() { |
| checkUrl("market://1234") |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| |
| Assert.assertNotNull(mDelegate.startActivityIntent); |
| Assert.assertTrue(mDelegate.startActivityIntent.getScheme().startsWith("market")); |
| } |
| |
| @Test |
| @SmallTest |
| public void testMarketIntent_MarketNotInstalled() { |
| mDelegate.setCanResolveActivityForMarket(false); |
| checkUrl("market://1234").expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| } |
| |
| @Test |
| @SmallTest |
| public void testMarketIntent_ShowDialogIncognitoMarketInstalled() { |
| checkUrl("market://1234") |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_ASYNC_ACTION, |
| START_INCOGNITO | START_OTHER_ACTIVITY); |
| |
| Assert.assertTrue(mUrlHandler.mStartIncognitoIntentCalled); |
| } |
| |
| @Test |
| @SmallTest |
| public void testMarketIntent_DontShowDialogIncognitoMarketNotInstalled() { |
| mDelegate.setCanResolveActivityForMarket(false); |
| checkUrl("market://1234") |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| Assert.assertFalse(mUrlHandler.mStartIncognitoIntentCalled); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUserGesture_Regular() { |
| // 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) |
| .withHasUserGesture(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| Assert.assertTrue(mDelegate.maybeSetRequestMetadataCalled); |
| Assert.assertFalse(mUrlHandler.mStartIncognitoIntentCalled); |
| } |
| |
| @Test |
| @SmallTest |
| public void testUserGesture_Incognito() { |
| // 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) |
| .withHasUserGesture(true) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_ASYNC_ACTION, |
| START_INCOGNITO | START_OTHER_ACTIVITY); |
| Assert.assertTrue(mDelegate.maybeSetRequestMetadataCalled); |
| Assert.assertTrue(mUrlHandler.mStartIncognitoIntentCalled); |
| } |
| |
| @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) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| } |
| |
| @Test |
| @SmallTest |
| public void testAutofillAssistantIntentWithFallback_InIncognito() { |
| mDelegate.setIsIntentToAutofillAssistant(true); |
| checkUrl(AUTOFILL_ASSISTANT_INTENT_URL_WITH_FALLBACK) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| } |
| |
| @Test |
| @SmallTest |
| public void testAutofillAssistantIntentWithoutFallback_InRegular() { |
| mDelegate.setIsIntentToAutofillAssistant(true); |
| checkUrl(AUTOFILL_ASSISTANT_INTENT_URL_WITHOUT_FALLBACK) |
| .withIsIncognito(false) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| } |
| |
| @Test |
| @SmallTest |
| public void testAutofillAssistantIntentWithoutFallback_InIncognito() { |
| mDelegate.setIsIntentToAutofillAssistant(true); |
| checkUrl(AUTOFILL_ASSISTANT_INTENT_URL_WITHOUT_FALLBACK) |
| .withIsIncognito(true) |
| .expecting(OverrideUrlLoadingResult.NO_OVERRIDE, IGNORE); |
| |
| Assert.assertNull(mDelegate.startActivityIntent); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIntentActionMetrics() { |
| final String intentWithAction = |
| "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;" |
| + "action=android.intent.action.PICK;end"; |
| final String intentWithoutAction = |
| "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.android;end"; |
| |
| final int pickCount = RecordHistogram.getHistogramValueCountForTesting( |
| ExternalNavigationHandler.INTENT_ACTION_HISTOGRAM, |
| ExternalNavigationHandler.StandardActions.PICK); |
| final int viewCount = RecordHistogram.getHistogramValueCountForTesting( |
| ExternalNavigationHandler.INTENT_ACTION_HISTOGRAM, |
| ExternalNavigationHandler.StandardActions.VIEW); |
| |
| checkUrl(intentWithAction) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| Assert.assertEquals(pickCount + 1, |
| RecordHistogram.getHistogramValueCountForTesting( |
| ExternalNavigationHandler.INTENT_ACTION_HISTOGRAM, |
| ExternalNavigationHandler.StandardActions.PICK)); |
| Assert.assertEquals(viewCount, |
| RecordHistogram.getHistogramValueCountForTesting( |
| ExternalNavigationHandler.INTENT_ACTION_HISTOGRAM, |
| ExternalNavigationHandler.StandardActions.VIEW)); |
| |
| checkUrl(intentWithoutAction) |
| .expecting(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, |
| START_OTHER_ACTIVITY); |
| Assert.assertEquals(viewCount + 1, |
| RecordHistogram.getHistogramValueCountForTesting( |
| ExternalNavigationHandler.INTENT_ACTION_HISTOGRAM, |
| ExternalNavigationHandler.StandardActions.VIEW)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testAppIntentActionMetrics() { |
| final String appIntent = "android-app://com.google.zxing.client.android/zxing/scan/#Intent;" |
| + "action=android.intent.action.ANSWER;end"; |
| final int count = RecordHistogram.getHistogramValueCountForTesting( |
| ExternalNavigationHandler.INTENT_ACTION_HISTOGRAM, |
| ExternalNavigationHandler.StandardActions.ANSWER); |
| |
| checkUrl(appIntent).expecting( |
| OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT, START_OTHER_ACTIVITY); |
| Assert.assertEquals(count + 1, |
| RecordHistogram.getHistogramValueCountForTesting( |
| ExternalNavigationHandler.INTENT_ACTION_HISTOGRAM, |
| ExternalNavigationHandler.StandardActions.ANSWER)); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsDownload_noSystemDownloadManager() { |
| Assert.assertTrue("pdf should be a download, no viewer in Android Chrome", |
| mUrlHandler.isPdfDownload("http://somesampeleurldne.com/file.pdf")); |
| Assert.assertFalse("URL is not a file, but web page", |
| mUrlHandler.isPdfDownload("http://somesampleurldne.com/index.html")); |
| Assert.assertFalse("URL is not a file url", |
| mUrlHandler.isPdfDownload("http://somesampeleurldne.com/not.a.real.extension")); |
| Assert.assertFalse("URL is an image, can be viewed in Chrome", |
| mUrlHandler.isPdfDownload("http://somesampleurldne.com/image.jpg")); |
| Assert.assertFalse("URL is a text file can be viewed in Chrome", |
| mUrlHandler.isPdfDownload("http://somesampleurldne.com/copy.txt")); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializedHandler_NoResolveInfo() { |
| String packageName = ""; |
| List<ResolveInfo> resolveInfos = new ArrayList<ResolveInfo>(); |
| Assert.assertEquals(0, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializedHandler_NoPathOrAuthority() { |
| String packageName = ""; |
| ResolveInfo info = new ResolveInfo(); |
| info.filter = new IntentFilter(); |
| List<ResolveInfo> resolveInfos = makeResolveInfos(info); |
| Assert.assertEquals(0, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializedHandler_WithPath() { |
| String packageName = ""; |
| ResolveInfo info = new ResolveInfo(); |
| info.filter = new IntentFilter(); |
| info.filter.addDataPath("somepath", 2); |
| List<ResolveInfo> resolveInfos = makeResolveInfos(info); |
| Assert.assertEquals(1, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializedHandler_WithAuthority() { |
| String packageName = ""; |
| ResolveInfo info = new ResolveInfo(); |
| info.filter = new IntentFilter(); |
| info.filter.addDataAuthority("http://www.google.com", "80"); |
| List<ResolveInfo> resolveInfos = makeResolveInfos(info); |
| Assert.assertEquals(1, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializedHandler_WithAuthority_Wildcard_Host() { |
| String packageName = ""; |
| ResolveInfo info = new ResolveInfo(); |
| info.filter = new IntentFilter(); |
| info.filter.addDataAuthority("*", null); |
| List<ResolveInfo> resolveInfos = makeResolveInfos(info); |
| Assert.assertEquals(0, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| |
| ResolveInfo infoWildcardSubDomain = new ResolveInfo(); |
| infoWildcardSubDomain.filter = new IntentFilter(); |
| infoWildcardSubDomain.filter.addDataAuthority("http://*.google.com", "80"); |
| List<ResolveInfo> resolveInfosWildcardSubDomain = makeResolveInfos(infoWildcardSubDomain); |
| Assert.assertEquals(1, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter(resolveInfosWildcardSubDomain, |
| packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializedHandler_WithTargetPackage_Matching() { |
| String packageName = "com.android.chrome"; |
| ResolveInfo info = new ResolveInfo(); |
| info.filter = new IntentFilter(); |
| info.filter.addDataAuthority("http://www.google.com", "80"); |
| info.activityInfo = new ActivityInfo(); |
| info.activityInfo.packageName = packageName; |
| List<ResolveInfo> resolveInfos = makeResolveInfos(info); |
| Assert.assertEquals(1, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializedHandler_WithTargetPackage_NotMatching() { |
| String packageName = "com.android.chrome"; |
| ResolveInfo info = new ResolveInfo(); |
| info.filter = new IntentFilter(); |
| info.filter.addDataAuthority("http://www.google.com", "80"); |
| info.activityInfo = new ActivityInfo(); |
| info.activityInfo.packageName = "com.foo.bar"; |
| List<ResolveInfo> resolveInfos = makeResolveInfos(info); |
| Assert.assertEquals(0, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| @Test |
| @SmallTest |
| public void testIsPackageSpecializeHandler_withEphemeralResolver() { |
| String packageName = ""; |
| ResolveInfo info = new ResolveInfo(); |
| info.filter = new IntentFilter(); |
| info.filter.addDataPath("somepath", 2); |
| info.activityInfo = new ActivityInfo(); |
| |
| // See IntentUtils.isInstantAppResolveInfo |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
| info.isInstantAppAvailable = true; |
| } else { |
| info.activityInfo.name = IntentUtils.EPHEMERAL_INSTALLER_CLASS; |
| } |
| info.activityInfo.packageName = "com.google.android.gms"; |
| List<ResolveInfo> resolveInfos = makeResolveInfos(info); |
| // Whether ephemeral resolver is counted as a specialized handler should be dependent on the |
| // value passed for |handlesInstantAppLaunchingInternally|. |
| Assert.assertEquals(0, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter( |
| resolveInfos, packageName, HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| Assert.assertEquals(1, |
| ExternalNavigationHandler |
| .getSpecializedHandlersWithFilter(resolveInfos, packageName, |
| !HANDLES_INSTANT_APP_LAUNCHING_INTERNALLY) |
| .size()); |
| } |
| |
| private static List<ResolveInfo> makeResolveInfos(ResolveInfo... infos) { |
| return Arrays.asList(infos); |
| } |
| |
| private static ResolveInfo newResolveInfo(String packageName) { |
| ActivityInfo ai = new ActivityInfo(); |
| ai.packageName = packageName; |
| ai.name = "Name: " + packageName; |
| ResolveInfo ri = new ResolveInfo(); |
| ri.activityInfo = ai; |
| return ri; |
| } |
| |
| private static ResolveInfo newSpecializedResolveInfo( |
| String packageName, IntentActivity activity) { |
| ResolveInfo info = newResolveInfo(packageName); |
| info.filter = new IntentFilter(Intent.ACTION_VIEW); |
| info.filter.addDataAuthority(activity.mUrlPrefix, null); |
| return info; |
| } |
| |
| private static class IntentActivity { |
| private String mUrlPrefix; |
| private String mPackageName; |
| private boolean mIsWebApk; |
| |
| public IntentActivity(String urlPrefix, String packageName) { |
| mUrlPrefix = urlPrefix; |
| mPackageName = packageName; |
| } |
| |
| public String urlPrefix() { |
| return mUrlPrefix; |
| } |
| |
| public String packageName() { |
| return mPackageName; |
| } |
| |
| public boolean isSpecialized() { |
| // Specialized if URL prefix is more than just a scheme. |
| return Pattern.compile("[^:/]+://.+").matcher(mUrlPrefix).matches(); |
| } |
| } |
| |
| private static class ExternalNavigationHandlerForTesting extends ExternalNavigationHandler { |
| public String defaultSmsPackageName; |
| public String mLastCommittedUrl; |
| public boolean mIsSerpReferrer; |
| public boolean mShouldRequestFileAccess; |
| public String mNewUrlAfterClobbering; |
| public String mReferrerUrlForClobbering; |
| public boolean mStartFileIntentCalled; |
| public Intent mStartActivityInIncognitoIntent; |
| public boolean mStartIncognitoIntentCalled; |
| |
| public ExternalNavigationHandlerForTesting(ExternalNavigationDelegate delegate) { |
| super(delegate); |
| } |
| |
| @Override |
| protected boolean isValidWebApk(String packageName) { |
| return packageName.startsWith(WEBAPK_PACKAGE_PREFIX) |
| && !packageName.equals(INVALID_WEBAPK_PACKAGE_NAME); |
| } |
| |
| @Override |
| public boolean blockExternalFormRedirectsWithoutGesture() { |
| return true; |
| } |
| |
| @Override |
| protected boolean startIncognitoIntentInternal(Intent intent, String referrerUrl, |
| String fallbackUrl, boolean needsToCloseTab, boolean proxy) { |
| mStartActivityInIncognitoIntent = intent; |
| mStartIncognitoIntentCalled = true; |
| return true; |
| } |
| |
| @Override |
| protected String getDefaultSmsPackageNameFromSystem() { |
| return defaultSmsPackageName; |
| } |
| |
| @Override |
| protected String getLastCommittedUrl() { |
| return mLastCommittedUrl; |
| } |
| |
| @Override |
| protected boolean isSerpReferrer() { |
| return mIsSerpReferrer; |
| } |
| |
| @Override |
| protected boolean shouldRequestFileAccess(String url) { |
| return mShouldRequestFileAccess; |
| } |
| |
| @Override |
| protected void startFileIntent(Intent intent, String referrerUrl, boolean needsToCloseTab) { |
| mStartFileIntentCalled = true; |
| } |
| |
| @Override |
| protected @OverrideUrlLoadingResult int clobberCurrentTab(String url, String referrerUrl) { |
| mNewUrlAfterClobbering = url; |
| mReferrerUrlForClobbering = referrerUrl; |
| return OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB; |
| } |
| }; |
| |
| private static class TestExternalNavigationDelegate implements ExternalNavigationDelegate { |
| public List<ResolveInfo> queryIntentActivities(Intent intent) { |
| List<ResolveInfo> list = new ArrayList<>(); |
| String dataString = intent.getDataString(); |
| if (dataString == null) return list; |
| if (dataString.startsWith("http://") || dataString.startsWith("https://")) { |
| list.add(newResolveInfo("chrome")); |
| } |
| for (IntentActivity intentActivity : mIntentActivities) { |
| if (dataString.startsWith(intentActivity.urlPrefix())) { |
| list.add(newSpecializedResolveInfo( |
| intentActivity.packageName(), intentActivity)); |
| } |
| } |
| if (!list.isEmpty()) return list; |
| |
| String schemeString = intent.getData().getScheme(); |
| boolean isMarketScheme = schemeString != null && schemeString.startsWith("market"); |
| if (mCanResolveActivityForMarket && isMarketScheme) { |
| list.add(newResolveInfo("market")); |
| return list; |
| } |
| if (mCanResolveActivityForExternalSchemes && !isMarketScheme) { |
| list.add(newResolveInfo(intent.getData().getScheme())); |
| } |
| return list; |
| } |
| |
| @Override |
| public Context getContext() { |
| return null; |
| } |
| |
| @Override |
| public boolean willAppHandleIntent(Intent intent) { |
| String chromePackageName = ContextUtils.getApplicationContext().getPackageName(); |
| if (chromePackageName.equals(intent.getPackage()) |
| || (intent.getComponent() != null |
| && chromePackageName.equals(intent.getComponent().getPackageName()))) { |
| return true; |
| } |
| |
| List<ResolveInfo> resolveInfos = queryIntentActivities(intent); |
| return resolveInfos.size() == 1 |
| && resolveInfos.get(0).activityInfo.packageName.contains("chrome"); |
| } |
| |
| @Override |
| public boolean shouldDisableExternalIntentRequestsForUrl(String url) { |
| return mShouldDisableExternalIntentRequests; |
| } |
| |
| @Override |
| public boolean handlesInstantAppLaunchingInternally() { |
| return false; |
| } |
| |
| @Override |
| public void dispatchAuthenticatedIntent(Intent intent) {} |
| |
| @Override |
| public void didStartActivity(Intent intent) { |
| startActivityIntent = intent; |
| } |
| |
| @Override |
| public @StartActivityIfNeededResult int maybeHandleStartActivityIfNeeded( |
| Intent intent, boolean proxy) { |
| // For simplicity, don't distinguish between startActivityIfNeeded and startActivity |
| // until a test requires this distinction. |
| startActivityIntent = intent; |
| mCalledWithProxy = proxy; |
| return StartActivityIfNeededResult.HANDLED_WITH_ACTIVITY_START; |
| } |
| |
| @Override |
| public @OverrideUrlLoadingResult int handleIncognitoIntentTargetingSelf( |
| Intent intent, String referrerUrl, String fallbackUrl) { |
| handleIncognitoIntentTargetingSelfCalled = true; |
| if (mCanLoadUrlInTab) return OverrideUrlLoadingResult.OVERRIDE_WITH_CLOBBERING_TAB; |
| return OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT; |
| } |
| |
| @Override |
| public boolean supportsCreatingNewTabs() { |
| return false; |
| } |
| |
| @Override |
| public void loadUrlInNewTab(final String url, final boolean launchIncognito) {} |
| |
| @Override |
| public boolean canLoadUrlInCurrentTab() { |
| return false; |
| } |
| |
| @Override |
| public void closeTab() {} |
| |
| @Override |
| public boolean isIncognito() { |
| return false; |
| } |
| |
| @Override |
| public void loadUrlIfPossible(LoadUrlParams loadUrlParams) {} |
| |
| @Override |
| public void maybeSetWindowId(Intent intent) {} |
| |
| @Override |
| public void maybeSetPendingReferrer(Intent intent, String referrerUrl) { |
| // This is used in a test to check that ExternalNavigationHandler correctly passes |
| // this data to the delegate when the referrer URL is non-null. |
| intent.putExtra(Intent.EXTRA_REFERRER, Uri.parse(referrerUrl)); |
| } |
| |
| @Override |
| public void maybeAdjustInstantAppExtras(Intent intent, boolean isIntentToInstantApp) { |
| if (isIntentToInstantApp) { |
| intent.putExtra(IS_INSTANT_APP_EXTRA, true); |
| } else { |
| intent.putExtra(IS_INSTANT_APP_EXTRA, false); |
| } |
| } |
| |
| @Override |
| public void maybeSetRequestMetadata(Intent intent, boolean hasUserGesture, |
| boolean isRendererInitiated, Origin initiatorOrigin) { |
| maybeSetRequestMetadataCalled = true; |
| } |
| |
| @Override |
| public void maybeSetPendingIncognitoUrl(Intent intent) {} |
| |
| @Override |
| public boolean isApplicationInForeground() { |
| return mIsChromeAppInForeground; |
| } |
| |
| @Override |
| public boolean maybeLaunchInstantApp(String url, String referrerUrl, |
| boolean isIncomingRedirect, boolean isSerpReferrer) { |
| return mCanHandleWithInstantApp; |
| } |
| |
| @Override |
| public WindowAndroid getWindowAndroid() { |
| return null; |
| } |
| |
| @Override |
| public WebContents getWebContents() { |
| return null; |
| } |
| |
| @Override |
| public boolean hasValidTab() { |
| return false; |
| } |
| |
| @Override |
| public boolean canCloseTabOnIncognitoIntentLaunch() { |
| return false; |
| } |
| |
| @Override |
| public boolean isIntentForTrustedCallingApp(Intent intent) { |
| return mIsCallingAppTrusted; |
| } |
| |
| @Override |
| public boolean isIntentToInstantApp(Intent intent) { |
| return mIsIntentToInstantApp; |
| } |
| |
| @Override |
| public boolean isIntentToAutofillAssistant(Intent intent) { |
| return mIsIntentToAutofillAssistant; |
| } |
| |
| @Override |
| public boolean handleWithAutofillAssistant(ExternalNavigationParams params, |
| Intent targetIntent, String browserFallbackUrl, boolean isGoogleReferrer) { |
| return mHandleWithAutofillAssistant; |
| } |
| |
| public void reset() { |
| startActivityIntent = null; |
| startIncognitoIntentCalled = false; |
| handleIncognitoIntentTargetingSelfCalled = false; |
| mCalledWithProxy = false; |
| } |
| |
| public void add(IntentActivity handler) { |
| mIntentActivities.add(handler); |
| } |
| |
| public void setCanResolveActivityForExternalSchemes(boolean value) { |
| mCanResolveActivityForExternalSchemes = value; |
| } |
| |
| public void setCanResolveActivityForMarket(boolean value) { |
| mCanResolveActivityForMarket = value; |
| } |
| |
| public void setIsChromeAppInForeground(boolean value) { |
| mIsChromeAppInForeground = value; |
| } |
| |
| public void setReferrerWebappPackageName(String webappPackageName) { |
| mReferrerWebappPackageName = webappPackageName; |
| } |
| |
| public String getReferrerWebappPackageName() { |
| return mReferrerWebappPackageName; |
| } |
| |
| public void setCanHandleWithInstantApp(boolean value) { |
| mCanHandleWithInstantApp = value; |
| } |
| |
| public void setHandleIntentWithAutofillAssistant(boolean value) { |
| mHandleWithAutofillAssistant = value; |
| } |
| |
| public void setIsCallingAppTrusted(boolean trusted) { |
| mIsCallingAppTrusted = trusted; |
| } |
| |
| public void setDisableExternalIntentRequests(boolean disable) { |
| mShouldDisableExternalIntentRequests = disable; |
| } |
| |
| public void setIsIntentToInstantApp(boolean value) { |
| mIsIntentToInstantApp = value; |
| } |
| |
| public void setIsIntentToAutofillAssistant(boolean value) { |
| mIsIntentToAutofillAssistant = value; |
| } |
| |
| public void setCanLoadUrlInTab(boolean value) { |
| mCanLoadUrlInTab = value; |
| } |
| |
| public Intent startActivityIntent; |
| public boolean startIncognitoIntentCalled; |
| public boolean handleIncognitoIntentTargetingSelfCalled; |
| public boolean maybeSetRequestMetadataCalled; |
| |
| private String mReferrerWebappPackageName; |
| |
| private ArrayList<IntentActivity> mIntentActivities = new ArrayList<IntentActivity>(); |
| private boolean mCanResolveActivityForExternalSchemes = true; |
| private boolean mCanResolveActivityForMarket = true; |
| private boolean mCanHandleWithInstantApp; |
| private boolean mHandleWithAutofillAssistant; |
| public boolean mCalledWithProxy; |
| public boolean mIsChromeAppInForeground = true; |
| private boolean mIsCallingAppTrusted; |
| private boolean mShouldDisableExternalIntentRequests; |
| private boolean mIsIntentToInstantApp; |
| private boolean mIsIntentToAutofillAssistant; |
| private boolean mCanLoadUrlInTab; |
| } |
| |
| private void checkIntentSanity(Intent intent, String name) { |
| Assert.assertTrue("The invoked " + name + " doesn't have the BROWSABLE category set\n", |
| intent.hasCategory(Intent.CATEGORY_BROWSABLE)); |
| Assert.assertNull("The invoked " + name + " should not have a Component set\n", |
| intent.getComponent()); |
| } |
| |
| private ExternalNavigationTestParams checkUrl(String url) { |
| return new ExternalNavigationTestParams(url); |
| } |
| |
| private class ExternalNavigationTestParams { |
| private final String mUrl; |
| |
| private String mReferrerUrl; |
| private boolean mIsIncognito; |
| private int mPageTransition = PageTransition.LINK; |
| private boolean mIsRedirect; |
| private boolean mChromeAppInForegroundRequired = true; |
| private boolean mIsBackgroundTabNavigation; |
| private boolean mHasUserGesture; |
| private RedirectHandler mRedirectHandler; |
| private boolean mIsRendererInitiated; |
| private boolean mIsMainFrame = true; |
| |
| private ExternalNavigationTestParams(String url) { |
| mUrl = url; |
| } |
| |
| public ExternalNavigationTestParams withReferrer(String referrerUrl) { |
| mReferrerUrl = referrerUrl; |
| return this; |
| } |
| |
| public ExternalNavigationTestParams withIsIncognito(boolean isIncognito) { |
| mIsIncognito = isIncognito; |
| return this; |
| } |
| |
| public ExternalNavigationTestParams withPageTransition(int pageTransition) { |
| mPageTransition = pageTransition; |
| return this; |
| } |
| |
| public ExternalNavigationTestParams withIsRedirect(boolean isRedirect) { |
| mIsRedirect = isRedirect; |
| return this; |
| } |
| |
| public ExternalNavigationTestParams withHasUserGesture(boolean hasGesture) { |
| mHasUserGesture = hasGesture; |
| return this; |
| } |
| |
| public ExternalNavigationTestParams withChromeAppInForegroundRequired( |
| boolean foregroundRequired) { |
| mChromeAppInForegroundRequired = foregroundRequired; |
| return this; |
| } |
| |
| public ExternalNavigationTestParams withIsBackgroundTabNavigation( |
| boolean isBackgroundTabNavigation) { |
| mIsBackgroundTabNavigation = isBackgroundTabNavigation; |
| return this; |
| } |
| |
| public ExternalNavigationTestParams withRedirectHandler(RedirectHandler handler) { |
| mRedirectHandler = handler; |
| 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; |
| boolean expectStartActivity = |
| (otherExpectation & (START_WEBAPK | START_OTHER_ACTIVITY)) != 0; |
| boolean expectStartWebApk = (otherExpectation & START_WEBAPK) != 0; |
| boolean expectStartOtherActivity = (otherExpectation & START_OTHER_ACTIVITY) != 0; |
| boolean expectStartFile = (otherExpectation & START_FILE) != 0; |
| boolean expectSaneIntent = expectStartOtherActivity |
| && (otherExpectation & INTENT_SANITIZATION_EXCEPTION) == 0; |
| boolean expectProxyForIA = (otherExpectation & PROXY_FOR_INSTANT_APPS) != 0; |
| |
| mDelegate.reset(); |
| |
| ExternalNavigationParams params = |
| new ExternalNavigationParams |
| .Builder(mUrl, mIsIncognito, mReferrerUrl, mPageTransition, mIsRedirect) |
| .setApplicationMustBeInForeground(mChromeAppInForegroundRequired) |
| .setRedirectHandler(mRedirectHandler) |
| .setIsBackgroundTabNavigation(mIsBackgroundTabNavigation) |
| .setIsMainFrame(mIsMainFrame) |
| .setNativeClientPackageName(mDelegate.getReferrerWebappPackageName()) |
| .setHasUserGesture(mHasUserGesture) |
| .setIsRendererInitiated(mIsRendererInitiated) |
| .build(); |
| @OverrideUrlLoadingResult |
| int result = mUrlHandler.shouldOverrideUrlLoading(params); |
| boolean startActivityCalled = false; |
| boolean startWebApkCalled = false; |
| |
| // Incognito intent launching gets caught by the test URL handler, whereas non-incgonito |
| // intent launching gets caught by the test delegate. |
| Intent startActivityIntent = expectStartIncognito |
| ? mUrlHandler.mStartActivityInIncognitoIntent |
| : mDelegate.startActivityIntent; |
| |
| if (startActivityIntent != null) { |
| startActivityCalled = true; |
| String packageName = startActivityIntent.getPackage(); |
| if (packageName != null) { |
| startWebApkCalled = packageName.startsWith(WEBAPK_PACKAGE_PREFIX); |
| } |
| } |
| |
| Assert.assertEquals(expectedOverrideResult, result); |
| Assert.assertEquals(expectStartIncognito, mUrlHandler.mStartIncognitoIntentCalled); |
| Assert.assertEquals(expectStartActivity, startActivityCalled); |
| Assert.assertEquals(expectStartWebApk, startWebApkCalled); |
| Assert.assertEquals(expectStartFile, mUrlHandler.mStartFileIntentCalled); |
| Assert.assertEquals(expectProxyForIA, mDelegate.mCalledWithProxy); |
| |
| if (startActivityCalled && expectSaneIntent) { |
| checkIntentSanity(startActivityIntent, "Intent"); |
| if (startActivityIntent.getSelector() != null) { |
| checkIntentSanity(startActivityIntent.getSelector(), "Intent's selector"); |
| } |
| } |
| } |
| } |
| |
| private static String getPackageName() { |
| return ContextUtils.getApplicationContext().getPackageName(); |
| } |
| |
| private static class TestPackageManager extends MockPackageManager { |
| private TestExternalNavigationDelegate mDelegate; |
| |
| public TestPackageManager(TestExternalNavigationDelegate delegate) { |
| mDelegate = delegate; |
| } |
| |
| @Override |
| public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { |
| return mDelegate.queryIntentActivities(intent); |
| } |
| } |
| |
| private static class TestContext extends ContextWrapper { |
| private PackageManager mPackageManager; |
| |
| public TestContext(Context baseContext, TestExternalNavigationDelegate delegate) { |
| super(baseContext); |
| mPackageManager = new TestPackageManager(delegate); |
| } |
| |
| @Override |
| public Context getApplicationContext() { |
| return this; |
| } |
| |
| @Override |
| public PackageManager getPackageManager() { |
| return mPackageManager; |
| } |
| |
| @Override |
| public String getPackageName() { |
| return "test.app.name"; |
| } |
| |
| @Override |
| public void startActivities(Intent[] intents, Bundle options) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void startActivity(Intent intent) { |
| } |
| |
| @Override |
| public void startActivity(Intent intent, Bundle options) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| } |