Skip to content

Commit

Permalink
[Intent launching] Componentize incognito mode warning dialog
Browse files Browse the repository at this point in the history
This CL moves //chrome's behavior of warning the user when they are
about to leave incognito mode via an external intent down into
//components, thus having WebLayer benefit from this behavior as well.

Bug: 1063399
Change-Id: I03b2905b06de1d2eae536ee2fb73037c44f58900
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2287293
Reviewed-by: Michael Thiessen <mthiesse@chromium.org>
Commit-Queue: Colin Blundell <blundell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#787718}
  • Loading branch information
colinblundell authored and Commit Bot committed Jul 13, 2020
1 parent 6720d13 commit 1f6dc7a
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,20 @@
package org.chromium.chrome.browser.externalnav;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.provider.Browser;
import android.text.TextUtils;
import android.view.WindowManager.BadTokenException;

import androidx.annotation.Nullable;

import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.PackageManagerUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeTabbedActivity2;
import org.chromium.chrome.browser.IntentHandler;
Expand All @@ -47,7 +41,6 @@
import org.chromium.components.webapk.lib.client.WebApkValidator;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.url.Origin;

Expand Down Expand Up @@ -158,17 +151,6 @@ public void didStartActivity(Intent intent) {}
return StartActivityIfNeededResult.DID_NOT_HANDLE;
}

@Override
public boolean startIncognitoIntent(final Intent intent, final String referrerUrl,
final String fallbackUrl, final boolean needsToCloseTab, final boolean proxy) {
try {
return startIncognitoIntentInternal(
intent, referrerUrl, fallbackUrl, needsToCloseTab, proxy);
} catch (BadTokenException e) {
return false;
}
}

@Override
public @OverrideUrlLoadingResult int handleIncognitoIntentTargetingSelf(
final Intent intent, final String referrerUrl, final String fallbackUrl) {
Expand All @@ -179,58 +161,6 @@ public boolean startIncognitoIntent(final Intent intent, final String referrerUr
: OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT;
}

private boolean startIncognitoIntentInternal(final Intent intent, final String referrerUrl,
final String fallbackUrl, final boolean needsToCloseTab, final boolean proxy) {
if (!hasValidTab()) return false;
Context context = mTab.getWindowAndroid().getContext().get();
if (ContextUtils.activityFromContext(context) == null) return false;

new UiUtils.CompatibleAlertDialogBuilder(context, R.style.Theme_Chromium_AlertDialog)
.setTitle(R.string.external_app_leave_incognito_warning_title)
.setMessage(R.string.external_app_leave_incognito_warning)
.setPositiveButton(R.string.external_app_leave_incognito_leave,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
ExternalNavigationHandler.startActivity(
intent, proxy, ExternalNavigationDelegateImpl.this);
if (mTab != null && !mTab.isClosing() && mTab.isInitialized()
&& needsToCloseTab) {
closeTab();
}
} catch (ActivityNotFoundException e) {
// The activity that we thought was going to handle the intent
// no longer exists, so catch the exception and assume Chrome
// can handle it.
ExternalNavigationHandler.loadUrlFromIntent(referrerUrl,
fallbackUrl, intent.getDataString(),
ExternalNavigationDelegateImpl.this, needsToCloseTab,
true);
}
}
})
.setNegativeButton(R.string.external_app_leave_incognito_stay,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ExternalNavigationHandler.loadUrlFromIntent(referrerUrl,
fallbackUrl, intent.getDataString(),
ExternalNavigationDelegateImpl.this, needsToCloseTab, true);
}
})
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
ExternalNavigationHandler.loadUrlFromIntent(referrerUrl, fallbackUrl,
intent.getDataString(), ExternalNavigationDelegateImpl.this,
needsToCloseTab, true);
}
})
.show();
return true;
}

@Override
public boolean supportsCreatingNewTabs() {
return true;
Expand Down Expand Up @@ -385,6 +315,11 @@ public boolean hasValidTab() {
return mTab != null && !mIsTabDestroyed;
}

@Override
public boolean canCloseTabOnIncognitoIntentLaunch() {
return (mTab != null && !mTab.isClosing() && mTab.isInitialized());
}

@Override
public boolean isIntentForTrustedCallingApp(Intent intent) {
return false;
Expand Down
12 changes: 0 additions & 12 deletions chrome/browser/ui/android/strings/android_chrome_strings.grd
Original file line number Diff line number Diff line change
Expand Up @@ -2323,18 +2323,6 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
Turn on sync
</message>

<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING" desc="Alert dialog text warning the user (who is currently in incognito mode) that the site they are currently using is going to share data with an external application." formatter_data="android_java">
This site is about to share information with an app outside of incognito mode.
</message>
<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING_TITLE" desc="Title for dialog asking if the user wants to leave incognito mode. [CHAR-LIMIT=32]" formatter_data="android_java">
Leave incognito mode?
</message>
<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_STAY" desc="Label for the dialog button to stay in incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
Stay
</message>
<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_LEAVE" desc="Label for the dialog button to leave incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
Leave
</message>
<message name="IDS_EXTERNAL_APP_RESTRICTED_ACCESS_ERROR" desc="A message shown to the user if Chrome receives a file view request to something Chrome does not have access to view." formatter_data="android_java">
Chrome does not have access to the requested resource.
</message>
Expand Down
15 changes: 14 additions & 1 deletion components/browser_ui/strings/android/browser_ui_strings.grd
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,20 @@
<message name="IDS_BOTTOM_BAR_SCREEN_POSITION" desc="Accessibility label to inform users about the InfoBar location">
Options available near bottom of the screen
</message>
</messages>

<!-- Warning on sharing info with external apps in incognito mode -->
<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING" desc="Alert dialog text warning the user (who is currently in incognito mode) that the site they are currently using is going to share data with an external application." formatter_data="android_java">
This site is about to share information with an app outside of incognito mode.
</message>
<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_WARNING_TITLE" desc="Title for dialog asking if the user wants to leave incognito mode. [CHAR-LIMIT=32]" formatter_data="android_java">
Leave incognito mode?
</message>
<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_STAY" desc="Label for the dialog button to stay in incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
Stay
</message>
<message name="IDS_EXTERNAL_APP_LEAVE_INCOGNITO_LEAVE" desc="Label for the dialog button to leave incognito mode. [CHAR-LIMIT=20]" formatter_data="android_java">
Leave
</message>
</messages>
</release>
</grit>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b8efdca4a145257a2f18123938a46ce7c03cd7b8
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b8efdca4a145257a2f18123938a46ce7c03cd7b8
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b8efdca4a145257a2f18123938a46ce7c03cd7b8
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b8efdca4a145257a2f18123938a46ce7c03cd7b8
6 changes: 5 additions & 1 deletion components/external_intents/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ android_library("java") {
"//content/public/android:content_java",
"//services/network/public/mojom:mojom_java",
"//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/android_deps:androidx_appcompat_appcompat_java",
"//ui/android:ui_java",
"//url:gurl_java",
"//url:origin_java",
Expand All @@ -36,7 +37,10 @@ android_library("java") {
android_resources("java_resources") {
sources = []
custom_package = "org.chromium.components.external_intents"
deps = [ "//components/browser_ui/strings/android:browser_ui_strings_grd" ]
deps = [
"//components/browser_ui/strings/android:browser_ui_strings_grd",
"//components/browser_ui/styles/android:java_resources",
]
}

generate_jni("jni_headers") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,22 +94,6 @@ public interface ExternalNavigationDelegate {
@StartActivityIfNeededResult
int maybeHandleStartActivityIfNeeded(Intent intent, boolean proxy);

/**
* Display a dialog warning the user that they may be leaving this app by starting this
* intent. Give the user the opportunity to cancel the action. And if it is canceled, a
* navigation will happen in this app. Catches BadTokenExceptions caused by showing the dialog
* on certain devices. (crbug.com/782602)
* @param intent The intent for external application that will be sent.
* @param referrerUrl The referrer for the current navigation.
* @param fallbackUrl The URL to load if the user doesn't proceed with external intent.
* @param needsToCloseTab Whether the current tab has to be closed after the intent is sent.
* @param proxy Whether we need to proxy the intent through AuthenticatedProxyActivity (this is
* used by Instant Apps intents.
* @return True if the function returned error free, false if it threw an exception.
*/
boolean startIncognitoIntent(Intent intent, String referrerUrl, String fallbackUrl,
boolean needsToCloseTab, boolean proxy);

/**
* Handle the incognito intent by loading it as a URL in the embedder, using the fallbackUrl if
* the intent URL cannot be handled by the embedder.
Expand Down Expand Up @@ -185,6 +169,15 @@ boolean maybeLaunchInstantApp(
*/
boolean hasValidTab();

/**
* @return Whether it's possible to close the current tab on launching on an incognito intent.
* TODO(blundell): Investigate whether it would be feasible to change the //chrome
* implementation of this method to be identical to that of its implementation of
* ExternalNavigationDelegate#hasValidTab() and then eliminate this method in favor of
* ExternalNavigationHandler calling hasValidTab() if so.
*/
boolean canCloseTabOnIncognitoIntentLaunch();

/**
* @return whether this delegate supports creation of new tabs. If this method returns false,
* all URLs loaded by ExternalNavigationHandler will be loaded in the current tab and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
Expand All @@ -22,6 +25,7 @@
import android.provider.Telephony;
import android.text.TextUtils;
import android.util.Pair;
import android.view.WindowManager.BadTokenException;
import android.webkit.MimeTypeMap;
import android.webkit.WebView;

Expand Down Expand Up @@ -49,6 +53,7 @@
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.content_public.common.Referrer;
import org.chromium.network.mojom.ReferrerPolicy;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.PermissionCallback;
import org.chromium.url.URI;
Expand Down Expand Up @@ -948,8 +953,7 @@ private void prepareExternalIntent(Intent targetIntent, ExternalNavigationParams
boolean shouldProxyForInstantApps) {
// This intent may leave this app. Warn the user that incognito does not carry over
// to external apps.
if (mDelegate.startIncognitoIntent(targetIntent, params.getReferrerUrl(),
browserFallbackUrl,
if (startIncognitoIntent(targetIntent, params.getReferrerUrl(), browserFallbackUrl,
params.shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent(),
shouldProxyForInstantApps)) {
if (DEBUG) Log.i(TAG, "Incognito navigation out");
Expand All @@ -959,6 +963,81 @@ private void prepareExternalIntent(Intent targetIntent, ExternalNavigationParams
return OverrideUrlLoadingResult.NO_OVERRIDE;
}

/**
* Display a dialog warning the user that they may be leaving this app by starting this
* intent. Give the user the opportunity to cancel the action. And if it is canceled, a
* navigation will happen in this app. Catches BadTokenExceptions caused by showing the dialog
* on certain devices. (crbug.com/782602)
* @param intent The intent for external application that will be sent.
* @param referrerUrl The referrer for the current navigation.
* @param fallbackUrl The URL to load if the user doesn't proceed with external intent.
* @param needsToCloseTab Whether the current tab has to be closed after the intent is sent.
* @param proxy Whether we need to proxy the intent through AuthenticatedProxyActivity (this is
* used by Instant Apps intents.
* @return True if the function returned error free, false if it threw an exception.
*/
private boolean startIncognitoIntent(final Intent intent, final String referrerUrl,
final String fallbackUrl, final boolean needsToCloseTab, final boolean proxy) {
try {
return startIncognitoIntentInternal(
intent, referrerUrl, fallbackUrl, needsToCloseTab, proxy);
} catch (BadTokenException e) {
return false;
}
}

/**
* Internal implementation of startIncognitoIntent(), with all the same parameters.
*/
@VisibleForTesting
protected boolean startIncognitoIntentInternal(final Intent intent, final String referrerUrl,
final String fallbackUrl, final boolean needsToCloseTab, final boolean proxy) {
if (!mDelegate.hasValidTab()) return false;
Context context = mDelegate.getContext();
if (ContextUtils.activityFromContext(context) == null) return false;

new UiUtils.CompatibleAlertDialogBuilder(context, R.style.Theme_Chromium_AlertDialog)
.setTitle(R.string.external_app_leave_incognito_warning_title)
.setMessage(R.string.external_app_leave_incognito_warning)
.setPositiveButton(R.string.external_app_leave_incognito_leave,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
startActivity(intent, proxy, mDelegate);
if (mDelegate.canCloseTabOnIncognitoIntentLaunch()
&& needsToCloseTab) {
mDelegate.closeTab();
}
} catch (ActivityNotFoundException e) {
// The activity that we thought was going to handle the intent
// no longer exists, so catch the exception and assume Chrome
// can handle it.
loadUrlFromIntent(referrerUrl, fallbackUrl,
intent.getDataString(), mDelegate, needsToCloseTab,
true);
}
}
})
.setNegativeButton(R.string.external_app_leave_incognito_stay,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
loadUrlFromIntent(referrerUrl, fallbackUrl, intent.getDataString(),
mDelegate, needsToCloseTab, true);
}
})
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
loadUrlFromIntent(referrerUrl, fallbackUrl, intent.getDataString(),
mDelegate, needsToCloseTab, true);
}
})
.show();
return true;
}

/**
* If some third-party app launched this app with an intent, and the URL got redirected, and the
* user explicitly chose this app over other intent handlers, stay in the app unless there was a
Expand Down Expand Up @@ -1240,7 +1319,7 @@ private void sanitizeQueryIntentActivitiesIntent(Intent intent) {
}

if (params.isIncognito()) {
if (!mDelegate.startIncognitoIntent(intent, params.getReferrerUrl(), null,
if (!startIncognitoIntent(intent, params.getReferrerUrl(), null,

params.shouldCloseContentsOnOverrideUrlLoadingAndLaunchIntent(), false)) {
if (DEBUG) Log.i(TAG, "Failed to show incognito alert dialog.");
Expand Down
Loading

0 comments on commit 1f6dc7a

Please sign in to comment.