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