| 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.content.Context; | 9 import android.content.Context; |
| 10 import android.content.Intent; | 10 import android.content.Intent; |
| 11 import android.content.res.Resources; | 11 import android.content.res.Resources; |
| 12 import android.graphics.Bitmap; | 12 import android.graphics.Bitmap; |
| 13 import android.graphics.Point; | 13 import android.graphics.Point; |
| 14 import android.net.ConnectivityManager; | 14 import android.net.ConnectivityManager; |
| 15 import android.net.Uri; | 15 import android.net.Uri; |
| 16 import android.os.AsyncTask; | 16 import android.os.AsyncTask; |
| 17 import android.os.Binder; | 17 import android.os.Binder; |
| 18 import android.os.Build; | 18 import android.os.Build; |
| 19 import android.os.Bundle; | 19 import android.os.Bundle; |
| 20 import android.os.IBinder; | 20 import android.os.IBinder; |
| 21 import android.os.Process; | 21 import android.os.Process; |
| 22 import android.support.customtabs.CustomTabsIntent; | 22 import android.support.customtabs.CustomTabsIntent; |
| 23 import android.support.customtabs.ICustomTabsCallback; | 23 import android.support.customtabs.ICustomTabsCallback; |
| 24 import android.support.customtabs.ICustomTabsService; | 24 import android.support.customtabs.ICustomTabsService; |
| 25 import android.text.TextUtils; | 25 import android.text.TextUtils; |
| 26 import android.view.WindowManager; | 26 import android.view.WindowManager; |
| 27 | 27 |
| 28 import org.chromium.base.FieldTrialList; | 28 import org.chromium.base.FieldTrialList; |
| 29 import org.chromium.base.Log; | 29 import org.chromium.base.Log; |
| 30 import org.chromium.base.SysUtils; |
| 30 import org.chromium.base.ThreadUtils; | 31 import org.chromium.base.ThreadUtils; |
| 31 import org.chromium.base.VisibleForTesting; | 32 import org.chromium.base.VisibleForTesting; |
| 32 import org.chromium.base.annotations.SuppressFBWarnings; | 33 import org.chromium.base.annotations.SuppressFBWarnings; |
| 33 import org.chromium.base.library_loader.ProcessInitException; | 34 import org.chromium.base.library_loader.ProcessInitException; |
| 34 import org.chromium.chrome.R; | 35 import org.chromium.chrome.R; |
| 35 import org.chromium.chrome.browser.ChromeApplication; | 36 import org.chromium.chrome.browser.ChromeApplication; |
| 36 import org.chromium.chrome.browser.IntentHandler; | 37 import org.chromium.chrome.browser.IntentHandler; |
| 37 import org.chromium.chrome.browser.WarmupManager; | 38 import org.chromium.chrome.browser.WarmupManager; |
| 38 import org.chromium.chrome.browser.WebContentsFactory; | 39 import org.chromium.chrome.browser.WebContentsFactory; |
| 39 import org.chromium.chrome.browser.device.DeviceClassManager; | 40 import org.chromium.chrome.browser.device.DeviceClassManager; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 public boolean newSession(ICustomTabsCallback callback) { | 122 public boolean newSession(ICustomTabsCallback callback) { |
| 122 ClientManager.DisconnectCallback onDisconnect = new ClientManager.Discon
nectCallback() { | 123 ClientManager.DisconnectCallback onDisconnect = new ClientManager.Discon
nectCallback() { |
| 123 @Override | 124 @Override |
| 124 public void run(IBinder session) { | 125 public void run(IBinder session) { |
| 125 cancelPrerender(session); | 126 cancelPrerender(session); |
| 126 } | 127 } |
| 127 }; | 128 }; |
| 128 return mClientManager.newSession(callback, Binder.getCallingUid(), onDis
connect); | 129 return mClientManager.newSession(callback, Binder.getCallingUid(), onDis
connect); |
| 129 } | 130 } |
| 130 | 131 |
| 132 /** Warmup activities that should only happen once. */ |
| 133 @SuppressFBWarnings("DM_EXIT") |
| 134 private static void initializeBrowser(final ChromeApplication app) { |
| 135 ThreadUtils.assertOnUiThread(); |
| 136 try { |
| 137 app.startBrowserProcessesAndLoadLibrariesSync(true); |
| 138 } catch (ProcessInitException e) { |
| 139 Log.e(TAG, "ProcessInitException while starting the browser process.
"); |
| 140 // Cannot do anything without the native library, and cannot show a |
| 141 // dialog to the user. |
| 142 System.exit(-1); |
| 143 } |
| 144 final Context context = app.getApplicationContext(); |
| 145 new AsyncTask<Void, Void, Void>() { |
| 146 @Override |
| 147 protected Void doInBackground(Void... params) { |
| 148 ChildProcessLauncher.warmUp(context); |
| 149 return null; |
| 150 } |
| 151 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
| 152 ChromeBrowserInitializer.initNetworkChangeNotifier(context); |
| 153 WarmupManager.getInstance().initializeViewHierarchy( |
| 154 context, R.layout.custom_tabs_control_container); |
| 155 } |
| 156 |
| 131 @Override | 157 @Override |
| 132 public boolean warmup(long flags) { | 158 public boolean warmup(long flags) { |
| 159 return warmup(true); |
| 160 } |
| 161 |
| 162 /** |
| 163 * Starts as much as possible in anticipation of a future navigation. |
| 164 * |
| 165 * @param mayCreatesparewebcontents true if warmup() can create a spare rend
erer. |
| 166 * @return true for success. |
| 167 */ |
| 168 private boolean warmup(final boolean mayCreateSpareWebContents) { |
| 133 // Here and in mayLaunchUrl(), don't do expensive work for background ap
plications. | 169 // Here and in mayLaunchUrl(), don't do expensive work for background ap
plications. |
| 134 if (!isCallerForegroundOrSelf()) return false; | 170 if (!isCallerForegroundOrSelf()) return false; |
| 135 mClientManager.recordUidHasCalledWarmup(Binder.getCallingUid()); | 171 mClientManager.recordUidHasCalledWarmup(Binder.getCallingUid()); |
| 136 if (!mWarmupHasBeenCalled.compareAndSet(false, true)) return true; | 172 final boolean initialized = !mWarmupHasBeenCalled.compareAndSet(false, t
rue); |
| 137 // The call is non-blocking and this must execute on the UI thread, post
a task. | 173 // The call is non-blocking and this must execute on the UI thread, post
a task. |
| 138 ThreadUtils.postOnUiThread(new Runnable() { | 174 ThreadUtils.postOnUiThread(new Runnable() { |
| 139 @Override | 175 @Override |
| 140 @SuppressFBWarnings("DM_EXIT") | |
| 141 public void run() { | 176 public void run() { |
| 142 ChromeApplication app = (ChromeApplication) mApplication; | 177 if (!initialized) initializeBrowser((ChromeApplication) mApplica
tion); |
| 143 try { | 178 if (mayCreateSpareWebContents && mPrerender == null && !SysUtils
.isLowEndDevice()) { |
| 144 app.startBrowserProcessesAndLoadLibrariesSync(true); | 179 createSpareWebContents(); |
| 145 } catch (ProcessInitException e) { | |
| 146 Log.e(TAG, "ProcessInitException while starting the browser
process."); | |
| 147 // Cannot do anything without the native library, and cannot
show a | |
| 148 // dialog to the user. | |
| 149 System.exit(-1); | |
| 150 } | 180 } |
| 151 final Context context = app.getApplicationContext(); | |
| 152 new AsyncTask<Void, Void, Void>() { | |
| 153 @Override | |
| 154 protected Void doInBackground(Void... params) { | |
| 155 ChildProcessLauncher.warmUp(context); | |
| 156 return null; | |
| 157 } | |
| 158 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | |
| 159 ChromeBrowserInitializer.initNetworkChangeNotifier(context); | |
| 160 WarmupManager.getInstance().initializeViewHierarchy(app.getAppli
cationContext(), | |
| 161 R.layout.custom_tabs_control_container); | |
| 162 } | 181 } |
| 163 }); | 182 }); |
| 164 return true; | 183 return true; |
| 165 } | 184 } |
| 166 | 185 |
| 167 /** | 186 /** |
| 168 * Creates a spare {@link WebContents}, if none exists. | 187 * Creates a spare {@link WebContents}, if none exists. |
| 169 * | 188 * |
| 170 * Navigating to "about:blank" forces a lot of initialization to take place | 189 * Navigating to "about:blank" forces a lot of initialization to take place |
| 171 * here. This improves PLT. This navigation is never registered in the histo
ry, as | 190 * here. This improves PLT. This navigation is never registered in the histo
ry, as |
| (...skipping 11 matching lines...) Expand all Loading... |
| 183 } | 202 } |
| 184 | 203 |
| 185 @Override | 204 @Override |
| 186 public boolean mayLaunchUrl(ICustomTabsCallback callback, Uri url, final Bun
dle extras, | 205 public boolean mayLaunchUrl(ICustomTabsCallback callback, Uri url, final Bun
dle extras, |
| 187 List<Bundle> otherLikelyBundles) { | 206 List<Bundle> otherLikelyBundles) { |
| 188 // Don't do anything for unknown schemes. Not having a scheme is | 207 // Don't do anything for unknown schemes. Not having a scheme is |
| 189 // allowed, as we allow "www.example.com". | 208 // allowed, as we allow "www.example.com". |
| 190 String scheme = url.normalizeScheme().getScheme(); | 209 String scheme = url.normalizeScheme().getScheme(); |
| 191 if (scheme != null && !scheme.equals("http") && !scheme.equals("https"))
return false; | 210 if (scheme != null && !scheme.equals("http") && !scheme.equals("https"))
return false; |
| 192 // Things below need the browser process to be initialized. | 211 // Things below need the browser process to be initialized. |
| 193 if (!warmup(0)) return false; // Also does the foreground check. | 212 |
| 213 // Forbids warmup() from creating a spare renderer, as prerendering woul
dn't reuse |
| 214 // it. Checking whether prerendering is enabled requires the native libr
ary to be loaded, |
| 215 // which is not necessarily the case yet. |
| 216 if (!warmup(false)) return false; // Also does the foreground check. |
| 194 | 217 |
| 195 final IBinder session = callback.asBinder(); | 218 final IBinder session = callback.asBinder(); |
| 196 final String urlString = url.toString(); | 219 final String urlString = url.toString(); |
| 197 final boolean noPrerendering = | 220 final boolean noPrerendering = |
| 198 extras != null ? extras.getBoolean(NO_PRERENDERING_KEY, false) :
false; | 221 extras != null ? extras.getBoolean(NO_PRERENDERING_KEY, false) :
false; |
| 199 final int uid = Binder.getCallingUid(); | 222 final int uid = Binder.getCallingUid(); |
| 200 if (!mClientManager.updateStatsAndReturnWhetherAllowed(session, uid, url
String)) { | 223 if (!mClientManager.updateStatsAndReturnWhetherAllowed(session, uid, url
String)) { |
| 201 return false; | 224 return false; |
| 202 } | 225 } |
| 203 ThreadUtils.postOnUiThread(new Runnable() { | 226 ThreadUtils.postOnUiThread(new Runnable() { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 * | 259 * |
| 237 * TODO(lizeb): Update this when crbug.com/521729 is fixed. | 260 * TODO(lizeb): Update this when crbug.com/521729 is fixed. |
| 238 */ | 261 */ |
| 239 WebContents takeSpareWebContents() { | 262 WebContents takeSpareWebContents() { |
| 240 ThreadUtils.assertOnUiThread(); | 263 ThreadUtils.assertOnUiThread(); |
| 241 WebContents result = mSpareWebContents; | 264 WebContents result = mSpareWebContents; |
| 242 mSpareWebContents = null; | 265 mSpareWebContents = null; |
| 243 return result; | 266 return result; |
| 244 } | 267 } |
| 245 | 268 |
| 269 private void destroySpareWebContents() { |
| 270 ThreadUtils.assertOnUiThread(); |
| 271 WebContents webContents = takeSpareWebContents(); |
| 272 if (webContents != null) webContents.destroy(); |
| 273 } |
| 274 |
| 246 @Override | 275 @Override |
| 247 public boolean updateVisuals(final ICustomTabsCallback callback, Bundle bund
le) { | 276 public boolean updateVisuals(final ICustomTabsCallback callback, Bundle bund
le) { |
| 248 final Bundle actionButtonBundle = IntentUtils.safeGetBundle(bundle, | 277 final Bundle actionButtonBundle = IntentUtils.safeGetBundle(bundle, |
| 249 CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE); | 278 CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE); |
| 250 if (actionButtonBundle == null) return false; | 279 if (actionButtonBundle == null) return false; |
| 251 | 280 |
| 252 final Bitmap bitmap = ActionButtonParams.tryParseBitmapFromBundle(mAppli
cation, | 281 final Bitmap bitmap = ActionButtonParams.tryParseBitmapFromBundle(mAppli
cation, |
| 253 actionButtonBundle); | 282 actionButtonBundle); |
| 254 final String description = ActionButtonParams | 283 final String description = ActionButtonParams |
| 255 .tryParseDescriptionFromBundle(actionButtonBundle); | 284 .tryParseDescriptionFromBundle(actionButtonBundle); |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 474 // incompatible with prerendering through this service. Remove this | 503 // incompatible with prerendering through this service. Remove this |
| 475 // limitation, or remove ChromePrerenderService. | 504 // limitation, or remove ChromePrerenderService. |
| 476 WarmupManager.getInstance().disallowPrerendering(); | 505 WarmupManager.getInstance().disallowPrerendering(); |
| 477 // Ignores mayPrerender() for an empty URL, since it cancels an existing
prerender. | 506 // Ignores mayPrerender() for an empty URL, since it cancels an existing
prerender. |
| 478 if (!mayPrerender() && !TextUtils.isEmpty(url)) return; | 507 if (!mayPrerender() && !TextUtils.isEmpty(url)) return; |
| 479 if (!mWarmupHasBeenCalled.get()) return; | 508 if (!mWarmupHasBeenCalled.get()) return; |
| 480 // Last one wins and cancels the previous prerender. | 509 // Last one wins and cancels the previous prerender. |
| 481 cancelPrerender(null); | 510 cancelPrerender(null); |
| 482 if (TextUtils.isEmpty(url)) return; | 511 if (TextUtils.isEmpty(url)) return; |
| 483 if (!mClientManager.isPrerenderingAllowed(uid)) return; | 512 if (!mClientManager.isPrerenderingAllowed(uid)) return; |
| 513 |
| 514 // A prerender will be requested. Time to destroy the spare WebContents. |
| 515 destroySpareWebContents(); |
| 516 |
| 484 Intent extrasIntent = new Intent(); | 517 Intent extrasIntent = new Intent(); |
| 485 if (extras != null) extrasIntent.putExtras(extras); | 518 if (extras != null) extrasIntent.putExtras(extras); |
| 486 if (IntentHandler.getExtraHeadersFromIntent(extrasIntent) != null) retur
n; | 519 if (IntentHandler.getExtraHeadersFromIntent(extrasIntent) != null) retur
n; |
| 487 if (mExternalPrerenderHandler == null) { | 520 if (mExternalPrerenderHandler == null) { |
| 488 mExternalPrerenderHandler = new ExternalPrerenderHandler(); | 521 mExternalPrerenderHandler = new ExternalPrerenderHandler(); |
| 489 } | 522 } |
| 490 Point contentSize = estimateContentSize(); | 523 Point contentSize = estimateContentSize(); |
| 491 Context context = mApplication.getApplicationContext(); | 524 Context context = mApplication.getApplicationContext(); |
| 492 String referrer = IntentHandler.getReferrerUrlIncludingExtraHeaders(extr
asIntent, context); | 525 String referrer = IntentHandler.getReferrerUrlIncludingExtraHeaders(extr
asIntent, context); |
| 493 if (referrer == null && getReferrerForSession(session) != null) { | 526 if (referrer == null && getReferrerForSession(session) != null) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 529 screenSize.x /= density; | 562 screenSize.x /= density; |
| 530 screenSize.y /= density; | 563 screenSize.y /= density; |
| 531 return screenSize; | 564 return screenSize; |
| 532 } | 565 } |
| 533 | 566 |
| 534 @VisibleForTesting | 567 @VisibleForTesting |
| 535 void resetThrottling(Context context, int uid) { | 568 void resetThrottling(Context context, int uid) { |
| 536 mClientManager.resetThrottling(uid); | 569 mClientManager.resetThrottling(uid); |
| 537 } | 570 } |
| 538 } | 571 } |
| OLD | NEW |