blob: 8d073c4bb1a21ebd6a44eb360cce10da5b54ed34 [file] [log] [blame]
// Copyright 2016 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.chrome.browser.customtabs;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
import android.util.Base64;
import android.view.Menu;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ActivityState;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.LaunchIntentDispatcher;
import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory.CustomTabNavigationDelegate;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.InterceptNavigationDelegateTabHelper;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabDelegateFactory;
import org.chromium.chrome.browser.tab.TabTestUtils;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
import org.chromium.components.external_intents.InterceptNavigationDelegateImpl;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.DOMUtils;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.net.test.EmbeddedTestServerRule;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
/**
* Tests for external navigation handling of Custom Tabs generated by Chrome.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class CustomTabFromChromeExternalNavigationTest {
@Rule
public CustomTabActivityTestRule mActivityRule = new CustomTabActivityTestRule();
@Rule
public ChromeActivityTestRule mChromeActivityTestRule =
new ChromeActivityTestRule(ChromeTabbedActivity.class);
public EmbeddedTestServerRule mServerRule = new EmbeddedTestServerRule();
private Intent getCustomTabFromChromeIntent(final String url, final boolean markFromChrome) {
return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent = LaunchIntentDispatcher.createCustomTabActivityIntent(
InstrumentationRegistry.getTargetContext(), intent);
if (markFromChrome) {
// Explicitly not marking this as a trusted intent. If you add the trusted bit,
// then it prohibits launching external apps. We want to allow external apps
// to be launched, so we are intentionally not adding that for now. Ideally,
// being opened by Chrome would only be allowed with the corresponding trusted
// flag, but that requires additional refactoring in our external navigation
// handling.
intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_OPENED_BY_CHROME, true);
}
return intent;
});
}
private void startCustomTabFromChrome(String url) {
Intent intent = getCustomTabFromChromeIntent(url, true);
mActivityRule.startCustomTabActivityWithIntent(intent);
}
private void startPaymentRequestUIFromChrome(String url) {
Intent intent = getCustomTabFromChromeIntent(url, false);
CustomTabIntentDataProvider.addPaymentRequestUIExtras(intent);
mActivityRule.startCustomTabActivityWithIntent(intent);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@Test
@Feature("CustomTabFromChrome")
@MediumTest
public void testUsingStandardExternalNavigationHandler() {
startCustomTabFromChrome("about:blank");
Tab tab = mActivityRule.getActivity().getActivityTab();
TabDelegateFactory delegateFactory = TabTestUtils.getDelegateFactory(tab);
Assert.assertTrue(delegateFactory instanceof CustomTabDelegateFactory);
CustomTabDelegateFactory customTabDelegateFactory =
((CustomTabDelegateFactory) delegateFactory);
Assert.assertFalse(customTabDelegateFactory.getExternalNavigationDelegate()
instanceof CustomTabNavigationDelegate);
}
@Test
@Feature("CustomTabFromChrome")
@LargeTest
public void
testIntentWithRedirectToApp() {
final String redirectUrl = "https://maps.google.com/maps?q=1600+amphitheatre+parkway";
final String initialUrl =
mServerRule.getServer().getURL("/chrome/test/data/android/redirect/js_redirect.html"
+ "?replace_text="
+ Base64.encodeToString(
ApiCompatibilityUtils.getBytesUtf8("PARAM_URL"), Base64.URL_SAFE)
+ ":"
+ Base64.encodeToString(
ApiCompatibilityUtils.getBytesUtf8(redirectUrl), Base64.URL_SAFE));
mActivityRule.startActivityCompletely(getCustomTabFromChromeIntent(initialUrl, true));
mActivityRule.waitForActivityNativeInitializationComplete();
final AtomicReference<InterceptNavigationDelegateImpl> navigationDelegate =
new AtomicReference<>();
CriteriaHelper.pollUiThread(() -> {
return mActivityRule.getActivity().getActivityTab() != null;
}, "Tab never initialized.");
CriteriaHelper.pollUiThread(() -> {
Tab tab = mActivityRule.getActivity().getActivityTab();
InterceptNavigationDelegateImpl delegate =
InterceptNavigationDelegateTabHelper.get(tab);
if (delegate == null) return false;
navigationDelegate.set(delegate);
return true;
}, "Navigation delegate never initialized.");
CriteriaHelper.pollUiThread(
Criteria.equals(OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT,
navigationDelegate.get()::getLastOverrideUrlLoadingResultForTests));
CriteriaHelper.pollUiThread(() -> {
int state = ApplicationStatus.getStateForActivity(mActivityRule.getActivity());
return state == ActivityState.STOPPED || state == ActivityState.DESTROYED;
}, "Activity never stopped or destroyed.");
}
@Test
@Feature("CustomTabFromChrome")
@MediumTest
public void testIntentToOpenPaymentRequestUI() throws Exception {
startPaymentRequestUIFromChrome("about:blank");
Tab tab = mActivityRule.getActivity().getActivityTab();
TabDelegateFactory delegateFactory = TabTestUtils.getDelegateFactory(tab);
Assert.assertTrue(delegateFactory instanceof CustomTabDelegateFactory);
CustomTabDelegateFactory customTabDelegateFactory =
((CustomTabDelegateFactory) delegateFactory);
Assert.assertFalse(customTabDelegateFactory.getExternalNavigationDelegate()
instanceof CustomTabNavigationDelegate);
CustomTabsTestUtils.openAppMenuAndAssertMenuShown(mActivityRule.getActivity());
Menu menu = mActivityRule.getMenu();
Assert.assertTrue(menu.findItem(R.id.icon_row_menu_id).isVisible());
Assert.assertTrue(menu.findItem(R.id.find_in_page_id).isVisible());
Assert.assertFalse(menu.findItem(R.id.open_in_browser_id).isVisible());
Assert.assertFalse(menu.findItem(R.id.bookmark_this_page_id).isVisible());
Assert.assertFalse(menu.findItem(R.id.offline_page_id).isVisible());
Assert.assertFalse(menu.findItem(R.id.request_desktop_site_row_menu_id).isVisible());
Assert.assertFalse(menu.findItem(R.id.add_to_homescreen_id).isVisible());
Assert.assertFalse(menu.findItem(R.id.open_webapk_id).isVisible());
}
/**
* This test verifies that untrusted intents are not launched by PaymentHandlerActivity.
* The source of untrusted intent for our test is IntentURI.
*/
@Test
@LargeTest
public void testCCTOpenedFromIntentUriWithPaymentsUI() throws Exception {
final String initialUrl =
mServerRule.getServer().getURL("/chrome/test/data/android/url_overriding/"
+ "navigation_to_cct_via_intent_uri_spoofing.html");
mChromeActivityTestRule.startMainActivityOnBlankPage();
mChromeActivityTestRule.loadUrlInNewTab(initialUrl);
mChromeActivityTestRule.getActivity().onUserInteraction();
// Simulate the click on the link that fires the IntentURI for external navigations.
try {
DOMUtils.clickNode(mChromeActivityTestRule.getWebContents(), "link");
} catch (TimeoutException e) {
Assert.fail("Failed to click on the target node.");
return;
}
// We poll to check that a CustomTabActivity is fired because of our IntentURI.
// We also check that this CustomTabActivity should not be of PaymentHandlerActivity
// type as it lacks the trusted extras which can only be added by Chrome.
CriteriaHelper.pollUiThread(() -> {
boolean isCCTLaunched = false;
for (Activity runningActivity : ApplicationStatus.getRunningActivities()) {
if (runningActivity instanceof CustomTabActivity) {
isCCTLaunched = true;
CustomTabActivity cta = (CustomTabActivity) runningActivity;
if (PaymentHandlerActivity.class == cta.getClass()) return false;
}
}
return isCCTLaunched;
});
}
}