| 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 |