Commit ef64951d authored by Peter E Conn's avatar Peter E Conn Committed by Commit Bot

Reland "🤝 Return to verified origin when close button pressed."

This is a reland of fadba0e6

Original change's description:
> 🤝 Return to verified origin when close button pressed.
> 
> When a TWA leaves the verified origin, the Custom Tabs top bar is
> displayed and the user can see a close button. This close button would
> on a normal custom tab close the Activity.
> 
> On Trusted Web Activities, this should return us to the verified
> origin instead.
> 
> Bug: 907535
> Change-Id: I9f652a3255afdd23072b32c44db5680172ea33f2
> Reviewed-on: https://chromium-review.googlesource.com/c/1346463
> Commit-Queue: Peter Conn <peconn@chromium.org>
> Reviewed-by: Michael van Ouwerkerk <mvanouwerkerk@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#610399}

Bug: 907535
Change-Id: If6e17630d50ac283e91809f9f1b497b73e466e6b
Reviewed-on: https://chromium-review.googlesource.com/c/1348469Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Reviewed-by: default avatarMichael van Ouwerkerk <mvanouwerkerk@chromium.org>
Commit-Queue: Peter Conn <peconn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#610560}
parent 2f239a7c
......@@ -8,9 +8,11 @@ import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controll
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityDisclosureController;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityOpenTimeRecorder;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityToolbarController;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.view.PersistentNotificationView;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.view.TrustedWebActivityDisclosureView;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.view.TrustedWebActivityToolbarView;
import org.chromium.chrome.browser.customtabs.CloseButtonNavigator;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import javax.inject.Inject;
......@@ -29,7 +31,12 @@ public class TrustedWebActivityCoordinator {
TrustedWebActivityToolbarView toolbarView,
TrustedWebActivityDisclosureView disclosureView,
PersistentNotificationView notificationView,
TrustedWebActivityOpenTimeRecorder openTimeRecorder) {
// Do nothing for now, just resolve the classes that need to start working.
TrustedWebActivityOpenTimeRecorder openTimeRecorder,
TrustedWebActivityVerifier verifier,
CloseButtonNavigator closeButtonNavigator) {
// We don't need to do anything with most of the classes above, we just need to resolve them
// so they start working.
closeButtonNavigator.setLandingPageCriteria(verifier::isPageOnVerifiedOrigin);
}
}
......@@ -92,7 +92,8 @@ public class TrustedWebActivityVerifier implements NativeInitObserver {
CustomTabIntentDataProvider intentDataProvider,
CustomTabsConnection customTabsConnection,
ActivityLifecycleDispatcher lifecycleDispatcher,
TabObserverRegistrar tabObserverRegistrar, ActivityTabProvider activityTabProvider) {
TabObserverRegistrar tabObserverRegistrar,
ActivityTabProvider activityTabProvider) {
mClientAppDataRecorder = clientAppDataRecorder;
mCustomTabsConnection = customTabsConnection;
mIntentDataProvider = intentDataProvider;
......@@ -142,6 +143,11 @@ public class TrustedWebActivityVerifier implements NativeInitObserver {
}
}
/** Returns whether the given |url| is on an Origin that the package has been verified for. */
public boolean isPageOnVerifiedOrigin(String url) {
return OriginVerifier.isValidOrigin(mClientPackageName, new Origin(url), RELATIONSHIP);
}
/**
* Perform verification for the URL that the CustomTabActivity starts on.
*/
......
// Copyright 2018 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.support.annotation.Nullable;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.NavigationHistory;
import javax.inject.Inject;
/**
* Allows navigation to the most recent page that matches a criteria when the Custom Tabs close
* button is pressed. We call this page the landing page.
*
* For example, in Trusted Web Activities we only show the close button when the user has left the
* verified origin. If the user then pressed the close button, we want to navigate back to the
* verified origin instead of closing the Activity.
*
* Thread safety: Should only be called on UI thread.
* Native: Requires native.
*/
@ActivityScope
public class CloseButtonNavigator {
@Nullable private PageCriteria mLandingPageCriteria;
@Inject
public CloseButtonNavigator() {}
// TODO(peconn): Replace with Predicate<T> when we can use Java 8 libraries.
/** An interface that allows specifying if a URL matches some criteria. */
public interface PageCriteria {
/** Whether the given |url| matches the criteria. */
boolean matches(String url);
}
/** Sets the criteria for the page to go back to. */
public void setLandingPageCriteria(PageCriteria criteria) {
assert mLandingPageCriteria == null : "Conflicting criteria for close button navigation.";
mLandingPageCriteria = criteria;
}
/**
* Navigates to the most recent landing page. Returns {@code false} if no criteria for what is
* a landing page has been given or no such page can be found.
*/
public boolean navigateOnClose(@Nullable NavigationController controller) {
if (mLandingPageCriteria == null || controller == null) return false;
NavigationHistory history = controller.getNavigationHistory();
for (int i = history.getCurrentEntryIndex() - 1; i >= 0; i--) {
String url = history.getEntryAtIndex(i).getUrl();
if (!mLandingPageCriteria.matches(url)) continue;
controller.goToNavigationIndex(i);
return true;
}
return false;
}
}
......@@ -112,7 +112,6 @@ import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.NavigationEntry;
import org.chromium.content_public.browser.NavigationHistory;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.PageTransition;
......@@ -345,6 +344,9 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
moduleLoader.loadModule();
mModuleCallback = new LoadModuleCallback();
moduleLoader.addCallbackAndIncrementUseCount(mModuleCallback);
getComponent().resolveCloseButtonNavigator()
.setLandingPageCriteria(this::isModuleManagedUrl);
}
private boolean isModuleLoading() {
......@@ -381,26 +383,6 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
}
}
/**
* @return The index of the previous navigation history entry managed by a dynamic module
* or -1 if there is no such entry.
*/
private boolean goToModuleManagedNavigationIndex() {
if (mModuleActivityDelegate == null && mModuleCallback == null) return false;
NavigationController navigationController = getNavigationController();
if (navigationController == null) return false;
NavigationHistory history = navigationController.getNavigationHistory();
for (int i = history.getCurrentEntryIndex() - 1; i >= 0; i--) {
if (isModuleManagedUrl(history.getEntryAtIndex(i).getUrl())) {
navigationController.goToNavigationIndex(i);
return true;
}
}
return false;
}
@Nullable
private NavigationController getNavigationController() {
WebContents webContents = getActivityTab().getWebContents();
......@@ -629,7 +611,8 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
if (mIntentDataProvider.shouldEnableEmbeddedMediaExperience()) {
RecordUserAction.record("CustomTabs.CloseButtonClicked.DownloadsUI");
}
if (goToModuleManagedNavigationIndex()) {
if (getComponent().resolveCloseButtonNavigator()
.navigateOnClose(getNavigationController())) {
RecordUserAction.record(
"CustomTabs.CloseButtonClicked.GoToModuleManagedUrl");
return;
......
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.customtabs.dependency_injection;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityCoordinator;
import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule;
import org.chromium.chrome.browser.customtabs.CloseButtonNavigator;
import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
......@@ -22,5 +23,6 @@ import dagger.Subcomponent;
@ActivityScope
public interface CustomTabActivityComponent extends ChromeActivityComponent {
TrustedWebActivityCoordinator resolveTrustedWebActivityCoordinator();
CloseButtonNavigator resolveCloseButtonNavigator();
CustomTabDelegateFactory resolveTabDelegateFactory();
}
......@@ -412,6 +412,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/crypto/ByteArrayGenerator.java",
"java/src/org/chromium/chrome/browser/crypto/CipherFactory.java",
"java/src/org/chromium/chrome/browser/customtabs/ClientManager.java",
"java/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigator.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomButtonParams.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java",
"java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java",
......@@ -2331,6 +2332,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/contextual_suggestions/PageViewTimerTest.java",
"junit/src/org/chromium/chrome/browser/cookies/CanonicalCookieTest.java",
"junit/src/org/chromium/chrome/browser/crash/LogcatExtractionRunnableUnitTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java",
"junit/src/org/chromium/chrome/browser/display_cutout/DisplayCutoutControllerTest.java",
"junit/src/org/chromium/chrome/browser/download/DownloadSharedPreferenceEntryTest.java",
......
// Copyright 2018 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 static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.NavigationEntry;
import org.chromium.content_public.browser.NavigationHistory;
/**
* Tests for {@link CloseButtonNavigator}.
*/
@RunWith(BlockJUnit4ClassRunner.class)
public class CloseButtonNavigatorTest {
@Mock public NavigationController mNavigationController;
private final NavigationHistory mNavigationHistory = new NavigationHistory();
private final CloseButtonNavigator mCloseButtonNavigator = new CloseButtonNavigator();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mNavigationController.getNavigationHistory()).thenReturn(mNavigationHistory);
}
private void addSitesToHistory(String... urls) {
for (String url : urls) {
mNavigationHistory.addEntry(new NavigationEntry(0, url, "", "", "", "", null, 0));
}
// Point to the most recent entry in history.
mNavigationHistory.setCurrentEntryIndex(mNavigationHistory.getEntryCount() - 1);
}
/** Example criteria. */
private static boolean isRed(String url) {
return url.contains("red");
}
@Test
public void noCriteria() {
addSitesToHistory(
"www.blue.com/page1",
"www.blue.com/page2"
);
mCloseButtonNavigator.navigateOnClose(mNavigationController);
verify(mNavigationController, never()).goToNavigationIndex(anyInt());
}
@Test
public void matchingUrl() {
addSitesToHistory(
"www.red.com/page1",
"www.red.com/page2",
"www.blue.com/page1",
"www.blue.com/page2"
);
mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
mCloseButtonNavigator.navigateOnClose(mNavigationController);
verify(mNavigationController).goToNavigationIndex(eq(1)); // www.red.com/page2
// Verify that it wasn't called with any other index.
verify(mNavigationController).goToNavigationIndex(anyInt());
}
@Test
public void noMatchingUrl() {
addSitesToHistory(
"www.blue.com/page1",
"www.blue.com/page2"
);
mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
mCloseButtonNavigator.navigateOnClose(mNavigationController);
verify(mNavigationController, never()).goToNavigationIndex(anyInt());
}
@Test
public void inMiddleOfHistory() {
addSitesToHistory(
"www.red.com/page1",
"www.red.com/page2",
"www.blue.com/page1",
"www.blue.com/page2",
"www.red.com/page3"
);
mNavigationHistory.setCurrentEntryIndex(3); // www.blue.com/page2
mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
mCloseButtonNavigator.navigateOnClose(mNavigationController);
verify(mNavigationController).goToNavigationIndex(eq(1)); // www.red.com/page2
// Verify that it wasn't called with any other index.
verify(mNavigationController).goToNavigationIndex(anyInt());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment