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 |