1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.chrome.browser;
6
7import android.text.TextUtils;
8
9import org.chromium.base.CollectionUtil;
10
11import java.net.URI;
12import java.net.URISyntaxException;
13import java.util.HashSet;
14
15/**
16 * Utilities for working with URIs (and URLs). These methods may be used in security-sensitive
17 * contexts (after all, origins are the security boundary on the web), and so the correctness bar
18 * must be high.
19 */
20public class UrlUtilities {
21    /**
22     * URI schemes that ContentView can handle.
23     */
24    private static final HashSet<String> ACCEPTED_SCHEMES = CollectionUtil.newHashSet(
25        "about", "data", "file", "http", "https", "inline", "javascript");
26
27    /**
28     * URI schemes that Chrome can download.
29     */
30    private static final HashSet<String> DOWNLOADABLE_SCHEMES = CollectionUtil.newHashSet(
31        "data", "filesystem", "http", "https");
32
33    /**
34     * @param uri A URI.
35     *
36     * @return True if the URI's scheme is one that ContentView can handle.
37     */
38    public static boolean isAcceptedScheme(URI uri) {
39        return ACCEPTED_SCHEMES.contains(uri.getScheme());
40    }
41
42    /**
43     * @param uri A URI.
44     *
45     * @return True if the URI's scheme is one that ContentView can handle.
46     */
47    public static boolean isAcceptedScheme(String uri) {
48        try {
49            return isAcceptedScheme(new URI(uri));
50        } catch (URISyntaxException e) {
51            return false;
52        }
53    }
54
55    /**
56     * @param uri A URI.
57     *
58     * @return True if the URI's scheme is one that Chrome can download.
59     */
60    public static boolean isDownloadableScheme(URI uri) {
61        return DOWNLOADABLE_SCHEMES.contains(uri.getScheme());
62    }
63
64    /**
65     * @param uri A URI.
66     *
67     * @return True if the URI's scheme is one that Chrome can download.
68     */
69    public static boolean isDownloadableScheme(String uri) {
70        try {
71            return isDownloadableScheme(new URI(uri));
72        } catch (URISyntaxException e) {
73            return false;
74        }
75    }
76
77    /**
78     * @param uri A URI to repair.
79     *
80     * @return A String representation of a URI that will be valid for loading in a ContentView.
81     */
82    public static String fixUrl(String uri) {
83        if (uri == null) return null;
84
85        try {
86            String fixedUri = uri.trim();
87            if (fixedUri.indexOf("://") == 0) {
88                return "http" + fixedUri;
89            }
90            if (fixedUri.indexOf(":") == -1) {
91                return "http://" + fixedUri;
92            }
93
94            URI parsed = new URI(fixedUri);
95            if (parsed.getScheme() == null) {
96                parsed = new URI(
97                        "http",
98                        null,  // userInfo
99                        parsed.getHost(),
100                        parsed.getPort(),
101                        parsed.getRawPath(),
102                        parsed.getRawQuery(),
103                        parsed.getRawFragment());
104            }
105            return parsed.toString();
106        } catch (URISyntaxException e) {
107            // Can't do anything.
108            return uri;
109        }
110    }
111
112    /**
113     * Refer to UrlFixerUpper::FixupURL.
114     *
115     * Compare to {@link #fixUrl(String)}, This fixes URL more aggressively including Chrome
116     * specific cases. For example, "about:" becomes "chrome://version/". However, this is not a
117     * superset of {@link #fixUrl(String)} either. For example, this function doesn't do anything
118     * with "://mail.google.com:/", while the other one prepends "http". Also, for
119     * "//mail.google.com:/", this function prepends "file" while the other one prepends "http".
120     */
121    public static String fixupUrl(String uri) {
122        return nativeFixupUrl(uri, null);
123    }
124
125    /**
126     * Builds a String that strips down the URL to the its scheme, host, and port.
127     * @param uri URI to break down.
128     * @param showScheme Whether or not to show the scheme.  If the URL can't be parsed, this value
129     *                   is ignored.
130     * @return Stripped-down String containing the essential bits of the URL, or the original URL if
131     *         it fails to parse it.
132     */
133    public static String getOriginForDisplay(URI uri, boolean showScheme) {
134        String scheme = uri.getScheme();
135        String host = uri.getHost();
136        int port = uri.getPort();
137
138        String displayUrl;
139        if (TextUtils.isEmpty(scheme) || TextUtils.isEmpty(host)) {
140            displayUrl = uri.toString();
141        } else {
142            if (showScheme) {
143                scheme += "://";
144            } else {
145                scheme = "";
146            }
147
148            if (port == -1 || (port == 80 && "http".equals(scheme))
149                    || (port == 443 && "https".equals(scheme))) {
150                displayUrl = scheme + host;
151            } else {
152                displayUrl = scheme + host + ":" + port;
153            }
154        }
155
156        return displayUrl;
157    }
158
159    /**
160     * Determines whether or not the given URLs belong to the same broad domain or host.
161     * "Broad domain" is defined as the TLD + 1 or the host.
162     *
163     * For example, the TLD + 1 for http://news.google.com would be "google.com" and would be shared
164     * with other Google properties like http://finance.google.com.
165     *
166     * If {@code includePrivateRegistries} is marked as true, then private domain registries (like
167     * appspot.com) are considered "effective TLDs" -- all subdomains of appspot.com would be
168     * considered distinct (effective TLD = ".appspot.com" + 1).
169     * This means that http://chromiumreview.appspot.com and http://example.appspot.com would not
170     * belong to the same host.
171     * If {@code includePrivateRegistries} is false, all subdomains of appspot.com
172     * would be considered to be the same domain (TLD = ".com" + 1).
173     *
174     * @param primaryUrl First URL
175     * @param secondaryUrl Second URL
176     * @param includePrivateRegistries Whether or not to consider private registries.
177     * @return True iff the two URIs belong to the same domain or host.
178     */
179    public static boolean sameDomainOrHost(String primaryUrl, String secondaryUrl,
180            boolean includePrivateRegistries) {
181        return nativeSameDomainOrHost(primaryUrl, secondaryUrl, includePrivateRegistries);
182    }
183
184    /**
185     * This function works by calling net::registry_controlled_domains::GetDomainAndRegistry
186     *
187     * @param uri A URI
188     * @param includePrivateRegistries Whether or not to consider private registries.
189     *
190     * @return The registered, organization-identifying host and all its registry information, but
191     * no subdomains, from the given URI. Returns an empty string if the URI is invalid, has no host
192     * (e.g. a file: URI), has multiple trailing dots, is an IP address, has only one subcomponent
193     * (i.e. no dots other than leading/trailing ones), or is itself a recognized registry
194     * identifier.
195     */
196    public static String getDomainAndRegistry(String uri, boolean includePrivateRegistries) {
197        return nativeGetDomainAndRegistry(uri, includePrivateRegistries);
198    }
199
200    /**
201     * @param url A URL.
202     * @return Whether a given URL is one of [...]google.TLD or [...]youtube.TLD URLs.
203     */
204    public static boolean isGooglePropertyUrl(String url) {
205        if (TextUtils.isEmpty(url)) return false;
206        return nativeIsGooglePropertyUrl(url);
207    }
208
209    private static native boolean nativeSameDomainOrHost(String primaryUrl, String secondaryUrl,
210            boolean includePrivateRegistries);
211    private static native String nativeGetDomainAndRegistry(String url,
212            boolean includePrivateRegistries);
213    public static native boolean nativeIsGoogleSearchUrl(String url);
214    public static native boolean nativeIsGoogleHomePageUrl(String url);
215    public static native String nativeFixupUrl(String url, String desiredTld);
216    private static native boolean nativeIsGooglePropertyUrl(String url);
217}
218