summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java120
1 files changed, 120 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java
new file mode 100644
index 0000000000..20a7b95f4d
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java
@@ -0,0 +1,120 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package org.mozilla.gecko.util;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.net.Uri;
+import java.net.URISyntaxException;
+import java.util.Locale;
+
+/** Utilities for Intents. */
+public class IntentUtils {
+ private IntentUtils() {}
+
+ /**
+ * Return a Uri instance which is equivalent to uri, but with a guaranteed-lowercase scheme as if
+ * the API level 16 method Uri.normalizeScheme had been called.
+ *
+ * @param uri The URI string to normalize.
+ * @return The corresponding normalized Uri.
+ */
+ private static Uri normalizeUriScheme(final Uri uri) {
+ final String scheme = uri.getScheme();
+ if (scheme == null) {
+ return uri;
+ }
+ final String lower = scheme.toLowerCase(Locale.ROOT);
+ if (lower.equals(scheme)) {
+ return uri;
+ }
+
+ // Otherwise, return a new URI with a normalized scheme.
+ return uri.buildUpon().scheme(lower).build();
+ }
+
+ /**
+ * Return a normalized Uri instance that corresponds to the given URI string with cross-API-level
+ * compatibility.
+ *
+ * @param aUri The URI string to normalize.
+ * @return The corresponding normalized Uri.
+ */
+ public static Uri normalizeUri(final String aUri) {
+ final Uri normUri =
+ normalizeUriScheme(
+ aUri.indexOf(':') >= 0 ? Uri.parse(aUri) : new Uri.Builder().scheme(aUri).build());
+ return normUri;
+ }
+
+ public static boolean isUriSafeForScheme(final String aUri) {
+ return isUriSafeForScheme(normalizeUri(aUri));
+ }
+
+ /**
+ * Verify whether the given URI is considered safe to load in respect to its scheme. Unsafe URIs
+ * should be blocked from further handling.
+ *
+ * @param aUri The URI instance to test.
+ * @return Whether the provided URI is considered safe in respect to its scheme.
+ */
+ public static boolean isUriSafeForScheme(final Uri aUri) {
+ final String scheme = aUri.getScheme();
+ if ("tel".equals(scheme) || "sms".equals(scheme)) {
+ // Bug 794034 - We don't want to pass MWI or USSD codes to the
+ // dialer, and ensure the Uri class doesn't parse a URI
+ // containing a fragment ('#')
+ final String number = aUri.getSchemeSpecificPart();
+ if (number.contains("#") || number.contains("*") || aUri.getFragment() != null) {
+ return false;
+ }
+ }
+
+ if (("intent".equals(scheme) || "android-app".equals(scheme))) {
+ // Bug 1356893 - Rject intents with file data schemes.
+ return getSafeIntent(aUri) != null;
+ }
+
+ return true;
+ }
+
+ /**
+ * Create a safe intent for the given URI. Intents with file data schemes are considered unsafe.
+ *
+ * @param aUri The URI for the intent.
+ * @return A safe intent for the given URI or null if URI is considered unsafe.
+ */
+ public static Intent getSafeIntent(final Uri aUri) {
+ final Intent intent;
+ try {
+ intent = Intent.parseUri(aUri.toString(), 0);
+ } catch (final URISyntaxException e) {
+ return null;
+ }
+
+ final Uri data = intent.getData();
+ if (data != null && "file".equals(normalizeUriScheme(data).getScheme())) {
+ return null;
+ }
+
+ // Only open applications which can accept arbitrary data from a browser.
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+
+ // Prevent site from explicitly opening our internal activities,
+ // which can leak data.
+ intent.setComponent(null);
+ nullIntentSelector(intent);
+
+ return intent;
+ }
+
+ // We create a separate method to better encapsulate the @TargetApi use.
+ @TargetApi(15)
+ private static void nullIntentSelector(final Intent intent) {
+ intent.setSelector(null);
+ }
+}