OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.externalnav; | 5 package org.chromium.chrome.browser.externalnav; |
6 | 6 |
7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
8 import android.app.Activity; | 8 import android.app.Activity; |
9 import android.content.ActivityNotFoundException; | 9 import android.content.ActivityNotFoundException; |
10 import android.content.ComponentName; | 10 import android.content.ComponentName; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 public ExternalNavigationHandler(ExternalNavigationDelegate delegate) { | 67 public ExternalNavigationHandler(ExternalNavigationDelegate delegate) { |
68 mDelegate = delegate; | 68 mDelegate = delegate; |
69 } | 69 } |
70 | 70 |
71 /** | 71 /** |
72 * Determines whether the URL needs to be sent as an intent to the system, | 72 * Determines whether the URL needs to be sent as an intent to the system, |
73 * and sends it, if appropriate. | 73 * and sends it, if appropriate. |
74 * @return Whether the URL generated an intent, caused a navigation in | 74 * @return Whether the URL generated an intent, caused a navigation in |
75 * current tab, or wasn't handled at all. | 75 * current tab, or wasn't handled at all. |
76 */ | 76 */ |
| 77 public OverrideUrlLoadingResult shouldOverrideUrlLoading(ExternalNavigationP
arams params) { |
| 78 Intent intent; |
| 79 // Perform generic parsing of the URI to turn it into an Intent. |
| 80 try { |
| 81 intent = Intent.parseUri(params.getUrl(), Intent.URI_INTENT_SCHEME); |
| 82 } catch (URISyntaxException ex) { |
| 83 Log.w(TAG, "Bad URI " + params.getUrl() + ": " + ex.getMessage()); |
| 84 return OverrideUrlLoadingResult.NO_OVERRIDE; |
| 85 } |
| 86 |
| 87 boolean hasBrowserFallbackUrl = false; |
| 88 String browserFallbackUrl = |
| 89 IntentUtils.safeGetStringExtra(intent, EXTRA_BROWSER_FALLBACK_UR
L); |
| 90 if (browserFallbackUrl != null |
| 91 && UrlUtilities.isValidForIntentFallbackNavigation(browserFallba
ckUrl)) { |
| 92 hasBrowserFallbackUrl = true; |
| 93 } else { |
| 94 browserFallbackUrl = null; |
| 95 } |
| 96 |
| 97 OverrideUrlLoadingResult result = shouldOverrideUrlLoadingInternal( |
| 98 params, intent, hasBrowserFallbackUrl, browserFallbackUrl); |
| 99 |
| 100 if (result == OverrideUrlLoadingResult.NO_OVERRIDE && hasBrowserFallback
Url |
| 101 && (params.getRedirectHandler() == null |
| 102 // For instance, if this is a chained fallback URL, we i
gnore it. |
| 103 || !params.getRedirectHandler().shouldNotOverrideUrlLoad
ing())) { |
| 104 return clobberCurrentTabWithFallbackUrl(browserFallbackUrl, params); |
| 105 } |
| 106 return result; |
| 107 } |
| 108 |
77 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) | 109 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) |
78 public OverrideUrlLoadingResult shouldOverrideUrlLoading(ExternalNavigationP
arams params) { | 110 private OverrideUrlLoadingResult shouldOverrideUrlLoadingInternal( |
| 111 ExternalNavigationParams params, Intent intent, boolean hasBrowserFa
llbackUrl, |
| 112 String browserFallbackUrl) { |
79 // http://crbug.com/441284 : Disallow firing external intent while Chrom
e is in the | 113 // http://crbug.com/441284 : Disallow firing external intent while Chrom
e is in the |
80 // background. | 114 // background. |
81 if (params.isApplicationMustBeInForeground() && !mDelegate.isChromeAppIn
Foreground()) { | 115 if (params.isApplicationMustBeInForeground() && !mDelegate.isChromeAppIn
Foreground()) { |
82 return OverrideUrlLoadingResult.NO_OVERRIDE; | 116 return OverrideUrlLoadingResult.NO_OVERRIDE; |
83 } | 117 } |
84 // http://crbug.com/464669 : Disallow firing external intent from backgr
ound tab. | 118 // http://crbug.com/464669 : Disallow firing external intent from backgr
ound tab. |
85 if (params.isBackgroundTabNavigation()) { | 119 if (params.isBackgroundTabNavigation()) { |
86 return OverrideUrlLoadingResult.NO_OVERRIDE; | 120 return OverrideUrlLoadingResult.NO_OVERRIDE; |
87 } | 121 } |
88 | 122 |
(...skipping 15 matching lines...) Expand all Loading... |
104 // This should be covered by not showing the picker if the core type i
s reload. | 138 // This should be covered by not showing the picker if the core type i
s reload. |
105 | 139 |
106 // http://crbug.com/164194 . A navigation forwards or backwards should n
ever trigger | 140 // http://crbug.com/164194 . A navigation forwards or backwards should n
ever trigger |
107 // the intent picker. | 141 // the intent picker. |
108 if (isForwardBackNavigation) { | 142 if (isForwardBackNavigation) { |
109 return OverrideUrlLoadingResult.NO_OVERRIDE; | 143 return OverrideUrlLoadingResult.NO_OVERRIDE; |
110 } | 144 } |
111 | 145 |
112 // http://crbug/331571 : Do not override a navigation started from user
typing. | 146 // http://crbug/331571 : Do not override a navigation started from user
typing. |
113 // http://crbug/424029 : Need to stay in Chrome for an intent heading ex
plicitly to Chrome. | 147 // http://crbug/424029 : Need to stay in Chrome for an intent heading ex
plicitly to Chrome. |
114 if (params.getRedirectHandler() != null | 148 if (params.getRedirectHandler() != null) { |
115 && params.getRedirectHandler().shouldStayInChrome()) { | 149 if (params.getRedirectHandler().shouldStayInChrome() |
116 return OverrideUrlLoadingResult.NO_OVERRIDE; | 150 || params.getRedirectHandler().shouldNotOverrideUrlLoading()
) { |
| 151 return OverrideUrlLoadingResult.NO_OVERRIDE; |
| 152 } |
117 } | 153 } |
118 | 154 |
119 // http://crbug.com/149218: We want to show the intent picker for ordina
ry links, providing | 155 // http://crbug.com/149218: We want to show the intent picker for ordina
ry links, providing |
120 // the link is not an incoming intent from another application, unless i
t's a redirect (see | 156 // the link is not an incoming intent from another application, unless i
t's a redirect (see |
121 // below). | 157 // below). |
122 boolean linkNotFromIntent = isLink && !isFromIntent; | 158 boolean linkNotFromIntent = isLink && !isFromIntent; |
123 | 159 |
124 boolean isOnEffectiveIntentRedirect = params.getRedirectHandler() == nul
l ? false | 160 boolean isOnEffectiveIntentRedirect = params.getRedirectHandler() == nul
l ? false |
125 : params.getRedirectHandler().isOnEffectiveIntentRedirectChain()
; | 161 : params.getRedirectHandler().isOnEffectiveIntentRedirectChain()
; |
126 | 162 |
(...skipping 15 matching lines...) Expand all Loading... |
142 // page, there is clear intent to complete the navigation in Chrome. | 178 // page, there is clear intent to complete the navigation in Chrome. |
143 if (params.getReferrerUrl() != null && params.getReferrerUrl().startsWit
h( | 179 if (params.getReferrerUrl() != null && params.getReferrerUrl().startsWit
h( |
144 UrlConstants.CHROME_SCHEME) && (params.getUrl().startsWith(UrlCo
nstants.HTTP_SCHEME) | 180 UrlConstants.CHROME_SCHEME) && (params.getUrl().startsWith(UrlCo
nstants.HTTP_SCHEME) |
145 || params.getUrl().startsWith(UrlConstants.HTTPS_SCHEME)
)) { | 181 || params.getUrl().startsWith(UrlConstants.HTTPS_SCHEME)
)) { |
146 return OverrideUrlLoadingResult.NO_OVERRIDE; | 182 return OverrideUrlLoadingResult.NO_OVERRIDE; |
147 } | 183 } |
148 | 184 |
149 if (params.getUrl().startsWith(SCHEME_WTAI_MC)) { | 185 if (params.getUrl().startsWith(SCHEME_WTAI_MC)) { |
150 // wtai://wp/mc;number | 186 // wtai://wp/mc;number |
151 // number=string(phone-number) | 187 // number=string(phone-number) |
152 Intent intent = new Intent(Intent.ACTION_VIEW, | 188 mDelegate.startActivity(new Intent(Intent.ACTION_VIEW, |
153 Uri.parse(WebView.SCHEME_TEL | 189 Uri.parse(WebView.SCHEME_TEL |
154 + params.getUrl().substring(SCHEME_WTAI_MC.length())
)); | 190 + params.getUrl().substring(SCHEME_WTAI_MC.length())
))); |
155 mDelegate.startActivity(intent); | |
156 return OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT; | 191 return OverrideUrlLoadingResult.OVERRIDE_WITH_EXTERNAL_INTENT; |
157 } else if (params.getUrl().startsWith(SCHEME_WTAI)) { | 192 } else if (params.getUrl().startsWith(SCHEME_WTAI)) { |
158 // TODO: handle other WTAI schemes. | 193 // TODO: handle other WTAI schemes. |
159 return OverrideUrlLoadingResult.NO_OVERRIDE; | 194 return OverrideUrlLoadingResult.NO_OVERRIDE; |
160 } | 195 } |
161 | 196 |
162 // The "about:" schemes are internal to the browser; don't want these to | 197 // The "about:" schemes are internal to the browser; don't want these to |
163 // be dispatched to other apps. | 198 // be dispatched to other apps. |
164 if (params.getUrl().startsWith("about:")) { | 199 if (params.getUrl().startsWith("about:")) { |
165 return OverrideUrlLoadingResult.NO_OVERRIDE; | 200 return OverrideUrlLoadingResult.NO_OVERRIDE; |
(...skipping 11 matching lines...) Expand all Loading... |
177 if (params.getUrl().matches(".*youtube\\.com.*[?&]pairingCode=.*")) { | 212 if (params.getUrl().matches(".*youtube\\.com.*[?&]pairingCode=.*")) { |
178 return OverrideUrlLoadingResult.NO_OVERRIDE; | 213 return OverrideUrlLoadingResult.NO_OVERRIDE; |
179 } | 214 } |
180 | 215 |
181 // TODO(changwan): check if we need to handle URL even when external int
ent is off. | 216 // TODO(changwan): check if we need to handle URL even when external int
ent is off. |
182 if (CommandLine.getInstance().hasSwitch( | 217 if (CommandLine.getInstance().hasSwitch( |
183 ChromeSwitches.DISABLE_EXTERNAL_INTENT_REQUESTS)) { | 218 ChromeSwitches.DISABLE_EXTERNAL_INTENT_REQUESTS)) { |
184 return OverrideUrlLoadingResult.NO_OVERRIDE; | 219 return OverrideUrlLoadingResult.NO_OVERRIDE; |
185 } | 220 } |
186 | 221 |
187 Intent intent; | |
188 // perform generic parsing of the URI to turn it into an Intent. | |
189 try { | |
190 intent = Intent.parseUri(params.getUrl(), Intent.URI_INTENT_SCHEME); | |
191 } catch (URISyntaxException ex) { | |
192 Log.w(TAG, "Bad URI " + params.getUrl() + ": " + ex.getMessage()); | |
193 return OverrideUrlLoadingResult.NO_OVERRIDE; | |
194 } | |
195 | |
196 boolean hasBrowserFallbackUrl = false; | |
197 String browserFallbackUrl = IntentUtils.safeGetStringExtra( | |
198 intent, EXTRA_BROWSER_FALLBACK_URL); | |
199 if (browserFallbackUrl != null | |
200 && UrlUtilities.isValidForIntentFallbackNavigation(browserFallba
ckUrl)) { | |
201 hasBrowserFallbackUrl = true; | |
202 } | |
203 | |
204 // check whether the intent can be resolved. If not, we will see | 222 // check whether the intent can be resolved. If not, we will see |
205 // whether we can download it from the Market. | 223 // whether we can download it from the Market. |
206 if (!mDelegate.canResolveActivity(intent)) { | 224 if (!mDelegate.canResolveActivity(intent)) { |
207 if (hasBrowserFallbackUrl) { | 225 if (hasBrowserFallbackUrl) { |
208 // NOTE: any further redirection from fall-back URL should not o
verride URL loading. | 226 return clobberCurrentTabWithFallbackUrl(browserFallbackUrl, para
ms); |
209 // Otherwise, it can be used in chain for fingerprinting multipl
e app installation | |
210 // status in one shot. In order to prevent this scenario, we not
ify redirection | |
211 // handler that redirection from the current navigation should s
tay in Chrome. | |
212 if (params.getRedirectHandler() != null) { | |
213 params.getRedirectHandler() | |
214 .setShouldStayInChromeUntilNewUrlLoading(); | |
215 } | |
216 return mDelegate.clobberCurrentTab(browserFallbackUrl, params.ge
tReferrerUrl(), | |
217 params.getTab()); | |
218 } | 227 } |
219 String packagename = intent.getPackage(); | 228 String packagename = intent.getPackage(); |
220 if (packagename != null) { | 229 if (packagename != null) { |
221 try { | 230 try { |
222 intent = new Intent(Intent.ACTION_VIEW, Uri.parse( | 231 intent = new Intent(Intent.ACTION_VIEW, Uri.parse( |
223 "market://details?id=" + packagename | 232 "market://details?id=" + packagename |
224 + "&referrer=" + mDelegate.getPackageName())); | 233 + "&referrer=" + mDelegate.getPackageName())); |
225 intent.addCategory(Intent.CATEGORY_BROWSABLE); | 234 intent.addCategory(Intent.CATEGORY_BROWSABLE); |
226 intent.setPackage("com.android.vending"); | 235 intent.setPackage("com.android.vending"); |
227 mDelegate.startActivity(intent); | 236 mDelegate.startActivity(intent); |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
334 } | 343 } |
335 } catch (ActivityNotFoundException ex) { | 344 } catch (ActivityNotFoundException ex) { |
336 // Ignore the error. If no application can handle the URL, | 345 // Ignore the error. If no application can handle the URL, |
337 // assume the browser can handle it. | 346 // assume the browser can handle it. |
338 } | 347 } |
339 | 348 |
340 return OverrideUrlLoadingResult.NO_OVERRIDE; | 349 return OverrideUrlLoadingResult.NO_OVERRIDE; |
341 } | 350 } |
342 | 351 |
343 /** | 352 /** |
| 353 * Clobber the current tab with fallback URL. |
| 354 * |
| 355 * @param browserFallbackUrl The fallback URL. |
| 356 * @param params The external navigation params. |
| 357 * @return {@link OverrideUrlLoadingResult} if the tab was clobbered, or we
launched an |
| 358 * intent. |
| 359 */ |
| 360 private OverrideUrlLoadingResult clobberCurrentTabWithFallbackUrl( |
| 361 String browserFallbackUrl, ExternalNavigationParams params) { |
| 362 // NOTE: any further redirection from fall-back URL should not override
URL loading. |
| 363 // Otherwise, it can be used in chain for fingerprinting multiple app in
stallation |
| 364 // status in one shot. In order to prevent this scenario, we notify redi
rection |
| 365 // handler that redirection from the current navigation should stay in C
hrome. |
| 366 if (params.getRedirectHandler() != null) { |
| 367 params.getRedirectHandler().setShouldNotOverrideUrlLoadingUntilNewUr
lLoading(); |
| 368 } |
| 369 return mDelegate.clobberCurrentTab( |
| 370 browserFallbackUrl, params.getReferrerUrl(), params.getTab()); |
| 371 } |
| 372 |
| 373 /** |
344 * @return Whether the |url| could be handled by an external application on
the system. | 374 * @return Whether the |url| could be handled by an external application on
the system. |
345 */ | 375 */ |
346 public boolean canExternalAppHandleUrl(String url) { | 376 public boolean canExternalAppHandleUrl(String url) { |
347 if (url.startsWith(SCHEME_WTAI_MC)) return true; | 377 if (url.startsWith(SCHEME_WTAI_MC)) return true; |
348 try { | 378 try { |
349 Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); | 379 Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); |
350 return intent.getPackage() != null || mDelegate.canResolveActivity(i
ntent); | 380 return intent.getPackage() != null || mDelegate.canResolveActivity(i
ntent); |
351 } catch (URISyntaxException ex) { | 381 } catch (URISyntaxException ex) { |
352 // Ignore the error. | 382 // Ignore the error. |
353 } | 383 } |
354 return false; | 384 return false; |
355 } | 385 } |
356 } | 386 } |
OLD | NEW |