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.customtabs; | 5 package org.chromium.chrome.browser.customtabs; |
6 | 6 |
7 import android.app.ActivityManager; | 7 import android.app.ActivityManager; |
8 import android.app.Application; | 8 import android.app.Application; |
9 import android.app.PendingIntent; | 9 import android.app.PendingIntent; |
10 import android.content.Context; | 10 import android.content.Context; |
(...skipping 10 matching lines...) Expand all Loading... |
21 import android.os.SystemClock; | 21 import android.os.SystemClock; |
22 import android.support.customtabs.CustomTabsCallback; | 22 import android.support.customtabs.CustomTabsCallback; |
23 import android.support.customtabs.CustomTabsIntent; | 23 import android.support.customtabs.CustomTabsIntent; |
24 import android.support.customtabs.CustomTabsService; | 24 import android.support.customtabs.CustomTabsService; |
25 import android.support.customtabs.CustomTabsSessionToken; | 25 import android.support.customtabs.CustomTabsSessionToken; |
26 import android.text.TextUtils; | 26 import android.text.TextUtils; |
27 import android.util.Pair; | 27 import android.util.Pair; |
28 import android.widget.RemoteViews; | 28 import android.widget.RemoteViews; |
29 | 29 |
30 import org.chromium.base.CommandLine; | 30 import org.chromium.base.CommandLine; |
| 31 import org.chromium.base.ContextUtils; |
31 import org.chromium.base.Log; | 32 import org.chromium.base.Log; |
32 import org.chromium.base.ThreadUtils; | 33 import org.chromium.base.ThreadUtils; |
33 import org.chromium.base.TimeUtils; | 34 import org.chromium.base.TimeUtils; |
34 import org.chromium.base.TraceEvent; | 35 import org.chromium.base.TraceEvent; |
35 import org.chromium.base.VisibleForTesting; | 36 import org.chromium.base.VisibleForTesting; |
36 import org.chromium.base.annotations.SuppressFBWarnings; | 37 import org.chromium.base.annotations.SuppressFBWarnings; |
37 import org.chromium.base.library_loader.ProcessInitException; | 38 import org.chromium.base.library_loader.ProcessInitException; |
38 import org.chromium.base.metrics.RecordHistogram; | 39 import org.chromium.base.metrics.RecordHistogram; |
39 import org.chromium.chrome.R; | 40 import org.chromium.chrome.R; |
40 import org.chromium.chrome.browser.AppHooks; | 41 import org.chromium.chrome.browser.AppHooks; |
41 import org.chromium.chrome.browser.ChromeApplication; | 42 import org.chromium.chrome.browser.ChromeApplication; |
42 import org.chromium.chrome.browser.ChromeFeatureList; | 43 import org.chromium.chrome.browser.ChromeFeatureList; |
43 import org.chromium.chrome.browser.IntentHandler; | 44 import org.chromium.chrome.browser.IntentHandler; |
44 import org.chromium.chrome.browser.WarmupManager; | 45 import org.chromium.chrome.browser.WarmupManager; |
45 import org.chromium.chrome.browser.device.DeviceClassManager; | 46 import org.chromium.chrome.browser.device.DeviceClassManager; |
46 import org.chromium.chrome.browser.init.ChromeBrowserInitializer; | 47 import org.chromium.chrome.browser.init.ChromeBrowserInitializer; |
47 import org.chromium.chrome.browser.metrics.PageLoadMetrics; | 48 import org.chromium.chrome.browser.metrics.PageLoadMetrics; |
48 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; | 49 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; |
49 import org.chromium.chrome.browser.preferences.PrefServiceBridge; | 50 import org.chromium.chrome.browser.preferences.PrefServiceBridge; |
50 import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler; | 51 import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler; |
51 import org.chromium.chrome.browser.profiles.Profile; | 52 import org.chromium.chrome.browser.profiles.Profile; |
52 import org.chromium.chrome.browser.tab.Tab; | 53 import org.chromium.chrome.browser.tab.Tab; |
53 import org.chromium.chrome.browser.util.IntentUtils; | 54 import org.chromium.chrome.browser.util.IntentUtils; |
54 import org.chromium.chrome.browser.util.UrlUtilities; | 55 import org.chromium.chrome.browser.util.UrlUtilities; |
55 import org.chromium.content.browser.ChildProcessLauncherHelper; | 56 import org.chromium.content.browser.ChildProcessLauncherHelper; |
56 import org.chromium.content_public.browser.LoadUrlParams; | 57 import org.chromium.content_public.browser.LoadUrlParams; |
57 import org.chromium.content_public.browser.WebContents; | 58 import org.chromium.content_public.browser.WebContents; |
58 import org.chromium.content_public.common.Referrer; | 59 import org.chromium.content_public.common.Referrer; |
| 60 import org.chromium.net.GURLUtils; |
59 | 61 |
60 import java.io.BufferedReader; | 62 import java.io.BufferedReader; |
61 import java.io.FileReader; | 63 import java.io.FileReader; |
62 import java.io.IOException; | 64 import java.io.IOException; |
63 import java.util.List; | 65 import java.util.List; |
64 import java.util.concurrent.Callable; | 66 import java.util.concurrent.Callable; |
65 import java.util.concurrent.ExecutionException; | 67 import java.util.concurrent.ExecutionException; |
66 import java.util.concurrent.atomic.AtomicBoolean; | 68 import java.util.concurrent.atomic.AtomicBoolean; |
67 import java.util.concurrent.atomic.AtomicReference; | 69 import java.util.concurrent.atomic.AtomicReference; |
68 | 70 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 static final String DEBUG_OVERRIDE_KEY = | 111 static final String DEBUG_OVERRIDE_KEY = |
110 "android.support.customtabs.maylaunchurl.DEBUG_OVERRIDE"; | 112 "android.support.customtabs.maylaunchurl.DEBUG_OVERRIDE"; |
111 private static final int NO_OVERRIDE = 0; | 113 private static final int NO_OVERRIDE = 0; |
112 @VisibleForTesting | 114 @VisibleForTesting |
113 static final int NO_PRERENDERING = 1; | 115 static final int NO_PRERENDERING = 1; |
114 @VisibleForTesting | 116 @VisibleForTesting |
115 static final int PREFETCH_ONLY = 2; | 117 static final int PREFETCH_ONLY = 2; |
116 @VisibleForTesting | 118 @VisibleForTesting |
117 static final int HIDDEN_TAB = 3; | 119 static final int HIDDEN_TAB = 3; |
118 | 120 |
| 121 // TODO(lizeb): Move to the support library. |
| 122 @VisibleForTesting |
| 123 static final String REDIRECT_ENDPOINT_KEY = "android.support.customtabs.REDI
RECT_ENDPOINT"; |
| 124 |
119 private static AtomicReference<CustomTabsConnection> sInstance = new AtomicR
eference<>(); | 125 private static AtomicReference<CustomTabsConnection> sInstance = new AtomicR
eference<>(); |
120 | 126 |
121 /** Holds the parameters for the current speculation. */ | 127 /** Holds the parameters for the current speculation. */ |
122 @VisibleForTesting | 128 @VisibleForTesting |
123 static final class SpeculationParams { | 129 static final class SpeculationParams { |
124 @VisibleForTesting | 130 @VisibleForTesting |
125 static final int NO_SPECULATION = 0; | 131 static final int NO_SPECULATION = 0; |
126 @VisibleForTesting | 132 @VisibleForTesting |
127 static final int PREFETCH = 1; | 133 static final int PREFETCH = 1; |
128 @VisibleForTesting | 134 @VisibleForTesting |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 } | 345 } |
340 } finally { | 346 } finally { |
341 TraceEvent.end("CustomTabsConnection.warmupInternal"); | 347 TraceEvent.end("CustomTabsConnection.warmupInternal"); |
342 } | 348 } |
343 mWarmupHasBeenFinished.set(true); | 349 mWarmupHasBeenFinished.set(true); |
344 } | 350 } |
345 }); | 351 }); |
346 return true; | 352 return true; |
347 } | 353 } |
348 | 354 |
349 /** @return the URL converted to string, or null if it's invalid. */ | 355 /** @return the URL or null if it's invalid. */ |
350 private static String checkAndConvertUri(Uri uri) { | 356 private boolean isValid(Uri uri) { |
351 if (uri == null) return null; | 357 if (uri == null) return false; |
352 // Don't do anything for unknown schemes. Not having a scheme is allowed
, as we allow | 358 // Don't do anything for unknown schemes. Not having a scheme is allowed
, as we allow |
353 // "www.example.com". | 359 // "www.example.com". |
354 String scheme = uri.normalizeScheme().getScheme(); | 360 String scheme = uri.normalizeScheme().getScheme(); |
355 boolean allowedScheme = scheme == null || scheme.equals("http") || schem
e.equals("https"); | 361 boolean allowedScheme = scheme == null || scheme.equals("http") || schem
e.equals("https"); |
356 if (!allowedScheme) return null; | 362 if (!allowedScheme) return false; |
357 return uri.toString(); | 363 return true; |
358 } | 364 } |
359 | 365 |
360 /** | 366 /** |
361 * High confidence mayLaunchUrl() call, that is: | 367 * High confidence mayLaunchUrl() call, that is: |
362 * - Tries to prerender if possible. | 368 * - Tries to prerender if possible. |
363 * - An empty URL cancels the current prerender if any. | 369 * - An empty URL cancels the current prerender if any. |
364 * - If prerendering is not possible, makes sure that there is a spare rende
rer. | 370 * - If prerendering is not possible, makes sure that there is a spare rende
rer. |
365 */ | 371 */ |
366 private void highConfidenceMayLaunchUrl(CustomTabsSessionToken session, | 372 private void highConfidenceMayLaunchUrl(CustomTabsSessionToken session, |
367 int uid, String url, Bundle extras, List<Bundle> otherLikelyBundles)
{ | 373 int uid, String url, Bundle extras, List<Bundle> otherLikelyBundles)
{ |
(...skipping 22 matching lines...) Expand all Loading... |
390 ThreadUtils.assertOnUiThread(); | 396 ThreadUtils.assertOnUiThread(); |
391 if (!preconnectUrls(likelyBundles)) return false; | 397 if (!preconnectUrls(likelyBundles)) return false; |
392 WarmupManager.getInstance().createSpareWebContents(); | 398 WarmupManager.getInstance().createSpareWebContents(); |
393 return true; | 399 return true; |
394 } | 400 } |
395 | 401 |
396 private boolean preconnectUrls(List<Bundle> likelyBundles) { | 402 private boolean preconnectUrls(List<Bundle> likelyBundles) { |
397 boolean atLeastOneUrl = false; | 403 boolean atLeastOneUrl = false; |
398 if (likelyBundles == null) return false; | 404 if (likelyBundles == null) return false; |
399 WarmupManager warmupManager = WarmupManager.getInstance(); | 405 WarmupManager warmupManager = WarmupManager.getInstance(); |
400 Profile profile = Profile.getLastUsedProfile(); | 406 Profile profile = Profile.getLastUsedProfile().getOriginalProfile(); |
401 for (Bundle bundle : likelyBundles) { | 407 for (Bundle bundle : likelyBundles) { |
402 Uri uri; | 408 Uri uri; |
403 try { | 409 try { |
404 uri = IntentUtils.safeGetParcelable(bundle, CustomTabsService.KE
Y_URL); | 410 uri = IntentUtils.safeGetParcelable(bundle, CustomTabsService.KE
Y_URL); |
405 } catch (ClassCastException e) { | 411 } catch (ClassCastException e) { |
406 continue; | 412 continue; |
407 } | 413 } |
408 String url = checkAndConvertUri(uri); | 414 if (isValid(uri)) { |
409 if (url != null) { | 415 warmupManager.maybePreconnectUrlAndSubResources(profile, uri.toS
tring()); |
410 warmupManager.maybePreconnectUrlAndSubResources(profile, url); | |
411 atLeastOneUrl = true; | 416 atLeastOneUrl = true; |
412 } | 417 } |
413 } | 418 } |
414 return atLeastOneUrl; | 419 return atLeastOneUrl; |
415 } | 420 } |
416 | 421 |
417 public boolean mayLaunchUrl(CustomTabsSessionToken session, Uri url, Bundle
extras, | 422 public boolean mayLaunchUrl(CustomTabsSessionToken session, Uri url, Bundle
extras, |
418 List<Bundle> otherLikelyBundles) { | 423 List<Bundle> otherLikelyBundles) { |
419 try { | 424 try { |
420 TraceEvent.begin("CustomTabsConnection.mayLaunchUrl"); | 425 TraceEvent.begin("CustomTabsConnection.mayLaunchUrl"); |
421 boolean success = mayLaunchUrlInternal(session, url, extras, otherLi
kelyBundles); | 426 boolean success = mayLaunchUrlInternal(session, url, extras, otherLi
kelyBundles); |
422 logCall("mayLaunchUrl(" + url + ")", success); | 427 logCall("mayLaunchUrl(" + url + ")", success); |
423 return success; | 428 return success; |
424 } finally { | 429 } finally { |
425 TraceEvent.end("CustomTabsConnection.mayLaunchUrl"); | 430 TraceEvent.end("CustomTabsConnection.mayLaunchUrl"); |
426 } | 431 } |
427 } | 432 } |
428 | 433 |
429 private boolean mayLaunchUrlInternal(final CustomTabsSessionToken session, U
ri url, | 434 private boolean mayLaunchUrlInternal(final CustomTabsSessionToken session, U
ri url, |
430 final Bundle extras, final List<Bundle> otherLikelyBundles) { | 435 final Bundle extras, final List<Bundle> otherLikelyBundles) { |
431 final boolean lowConfidence = | 436 final boolean lowConfidence = |
432 (url == null || TextUtils.isEmpty(url.toString())) && otherLikel
yBundles != null; | 437 (url == null || TextUtils.isEmpty(url.toString())) && otherLikel
yBundles != null; |
433 final String urlString = checkAndConvertUri(url); | 438 final String urlString = isValid(url) ? url.toString() : null; |
434 if (url != null && urlString == null && !lowConfidence) return false; | 439 if (url != null && urlString == null && !lowConfidence) return false; |
435 | 440 |
436 // Things below need the browser process to be initialized. | 441 // Things below need the browser process to be initialized. |
437 | 442 |
438 // Forbids warmup() from creating a spare renderer, as prerendering woul
dn't reuse | 443 // Forbids warmup() from creating a spare renderer, as prerendering woul
dn't reuse |
439 // it. Checking whether prerendering is enabled requires the native libr
ary to be loaded, | 444 // it. Checking whether prerendering is enabled requires the native libr
ary to be loaded, |
440 // which is not necessarily the case yet. | 445 // which is not necessarily the case yet. |
441 if (!warmupInternal(false)) return false; // Also does the foreground ch
eck. | 446 if (!warmupInternal(false)) return false; // Also does the foreground ch
eck. |
442 | 447 |
443 final int uid = Binder.getCallingUid(); | 448 final int uid = Binder.getCallingUid(); |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
718 SPECULATION_STATUS_ON_SWAP_BACKGROUND_TAB_NOT_MATCHE
D); | 723 SPECULATION_STATUS_ON_SWAP_BACKGROUND_TAB_NOT_MATCHE
D); |
719 tab.destroy(); | 724 tab.destroy(); |
720 } | 725 } |
721 } | 726 } |
722 } finally { | 727 } finally { |
723 TraceEvent.end("CustomTabsConnection.takeHiddenTab"); | 728 TraceEvent.end("CustomTabsConnection.takeHiddenTab"); |
724 } | 729 } |
725 return null; | 730 return null; |
726 } | 731 } |
727 | 732 |
| 733 /** |
| 734 * Called when an intent is handled by either an existing or a new CustomTab
Activity. |
| 735 * |
| 736 * @param session Session extracted from the intent. |
| 737 * @param url URL extracted from the intent. |
| 738 * @param intent incoming intent. |
| 739 */ |
| 740 void onHandledIntent(CustomTabsSessionToken session, String url, Intent inte
nt) { |
| 741 // For the preconnection to not be a no-op, we need more than just the n
ative library. |
| 742 Context context = ContextUtils.getApplicationContext(); |
| 743 if (!ChromeBrowserInitializer.getInstance(context).hasNativeInitializati
onCompleted()) { |
| 744 return; |
| 745 } |
| 746 if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_REDIRECT_PRECONNE
CT)) return; |
| 747 |
| 748 // Conditions: |
| 749 // - There is a valid redirect endpoint. |
| 750 // - The URL's origin is first party with respect to the app. |
| 751 Uri redirectEndpoint = intent.getParcelableExtra(REDIRECT_ENDPOINT_KEY); |
| 752 if (redirectEndpoint == null || !isValid(redirectEndpoint)) return; |
| 753 |
| 754 String origin = GURLUtils.getOrigin(url); |
| 755 if (origin == null) return; |
| 756 if (!mClientManager.isFirstPartyOriginForSession(session, Uri.parse(orig
in))) return; |
| 757 |
| 758 WarmupManager.getInstance().maybePreconnectUrlAndSubResources( |
| 759 Profile.getLastUsedProfile(), redirectEndpoint.toString()); |
| 760 } |
| 761 |
728 /** See {@link ClientManager#getReferrerForSession(CustomTabsSessionToken)}
*/ | 762 /** See {@link ClientManager#getReferrerForSession(CustomTabsSessionToken)}
*/ |
729 public Referrer getReferrerForSession(CustomTabsSessionToken session) { | 763 public Referrer getReferrerForSession(CustomTabsSessionToken session) { |
730 return mClientManager.getReferrerForSession(session); | 764 return mClientManager.getReferrerForSession(session); |
731 } | 765 } |
732 | 766 |
733 /** @see ClientManager#shouldHideDomainForSession(CustomTabsSessionToken) */ | 767 /** @see ClientManager#shouldHideDomainForSession(CustomTabsSessionToken) */ |
734 public boolean shouldHideDomainForSession(CustomTabsSessionToken session) { | 768 public boolean shouldHideDomainForSession(CustomTabsSessionToken session) { |
735 return mClientManager.shouldHideDomainForSession(session); | 769 return mClientManager.shouldHideDomainForSession(session); |
736 } | 770 } |
737 | 771 |
(...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1234 private static void recordSpeculationStatusOnStart(int status) { | 1268 private static void recordSpeculationStatusOnStart(int status) { |
1235 RecordHistogram.recordEnumeratedHistogram( | 1269 RecordHistogram.recordEnumeratedHistogram( |
1236 "CustomTabs.SpeculationStatusOnStart", status, SPECULATION_STATU
S_ON_START_MAX); | 1270 "CustomTabs.SpeculationStatusOnStart", status, SPECULATION_STATU
S_ON_START_MAX); |
1237 } | 1271 } |
1238 | 1272 |
1239 private static void recordSpeculationStatusOnSwap(int status) { | 1273 private static void recordSpeculationStatusOnSwap(int status) { |
1240 RecordHistogram.recordEnumeratedHistogram( | 1274 RecordHistogram.recordEnumeratedHistogram( |
1241 "CustomTabs.SpeculationStatusOnSwap", status, SPECULATION_STATUS
_ON_SWAP_MAX); | 1275 "CustomTabs.SpeculationStatusOnSwap", status, SPECULATION_STATUS
_ON_SWAP_MAX); |
1242 } | 1276 } |
1243 } | 1277 } |
OLD | NEW |