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 |