Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/tab/BackgroundContentViewHelper.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.tab;
6
7 import android.app.Activity;
8 import android.net.Uri;
9 import android.os.Handler;
10 import android.text.TextUtils;
11
12 import org.chromium.base.CalledByNative;
13 import org.chromium.base.FieldTrialList;
14 import org.chromium.base.metrics.RecordHistogram;
15 import org.chromium.base.metrics.RecordUserAction;
16 import org.chromium.chrome.browser.ChromeVersionInfo;
17 import org.chromium.chrome.browser.ContentViewUtil;
18 import org.chromium.chrome.browser.EmptyTabObserver;
19 import org.chromium.chrome.browser.Tab;
20 import org.chromium.chrome.browser.device.DeviceClassManager;
21 import org.chromium.components.web_contents_delegate_android.WebContentsDelegate Android;
22 import org.chromium.content.browser.ContentView;
23 import org.chromium.content.browser.ContentViewClient;
24 import org.chromium.content.browser.ContentViewCore;
25 import org.chromium.content.browser.RenderCoordinates;
26 import org.chromium.content_public.browser.GestureStateListener;
27 import org.chromium.content_public.browser.LoadUrlParams;
28 import org.chromium.content_public.browser.WebContents;
29 import org.chromium.content_public.browser.WebContentsObserver;
30 import org.chromium.content_public.common.Referrer;
31 import org.chromium.ui.base.WindowAndroid;
32
33 import java.net.MalformedURLException;
34 import java.net.URL;
35 import java.util.concurrent.TimeUnit;
36
37 /**
38 * Responsible for background content view creation and management.
39 */
40 public class BackgroundContentViewHelper extends EmptyTabObserver {
41 private static final String GOOGLE_PREVIEW_SERVER_PREFIX = "icl";
42
43 // Google preview staging and canary servers. Only to be used in dev builds.
44 private static final String GOOGLE_PREVIEW_SERVER_SUFFIX = ".googleuserconte nt.com";
45
46 private static final String GWS_CHROME_JOINT_EXPERIMENT_ID_STRING = "gcjeid" ;
47
48 private static final String GOOGLE_DOMAIN_PREFIX = "www.google.";
49
50 // Instant search clicks swap reasons.
51 // IMPORTANT: Do not renumber any existing constants (except the BOUNDARY on es). Keep these in
52 // sync with histograms.xml file.
53 private static final int SWAP_REASON_REGULAR = 0;
54 private static final int SWAP_REASON_TIMEOUT = 1;
55 private static final int SWAP_REASON_ABORT_ON_NAVIGATE = 2;
56 private static final int SWAP_REASON_ON_PREVIEW_FAIL = 3;
57 private static final int SWAP_REASON_ORIGINAL_FAIL = 4;
58 private static final int SWAP_REASON_FORCE = 5;
59 private static final int SWAP_REASON_BOUNDARY = 6;
60
61 // Instant search clicks scroll state while swapping.
62 // IMPORTANT: Do not renumber any existing constants (except the BOUNDARY on es). Keep these in
63 // sync with histograms.xml file.
64 private static final int PREVIEW_SCROLL_STATE_NO_SCROLL = 0;
65 private static final int PREVIEW_SCROLL_STATE_SCROLL = 1;
66 private static final int PREVIEW_SCROLL_STATE_SCROLL_AT_BOTTOM = 2;
67 private static final int PREVIEW_SCROLL_STATE_BOUNDARY = 3;
68
69 // Instant search clicks experiment state.
70 private static final int INSTANT_SEARCH_CLICKS_EXPERIMENT_DISABLED = 0;
71 private static final int INSTANT_SEARCH_CLICKS_EXPERIMENT_ENABLED = 1;
72 private static final int INSTANT_SEARCH_CLICKS_EXPERIMENT_CONTROL = 2;
73
74 /**
75 * The maximum amount of time to wait for the background page to swap. We fo rce the swap after
76 * this timeout.
77 */
78 private final int mSwapTimeoutMs;
79
80 /**
81 * The minimum number of renderer frames needed before swap is triggered.
82 */
83 private final int mMinRendererFramesNeededForSwap;
84
85 // Native pointer corresponding to the current object.
86 private long mNativeBackgroundContentViewHelperPtr;
87
88 // The content view core created to load url in background.
89 private ContentViewCore mContentViewCore;
90
91 // Android window used to created content view.
92 private final WindowAndroid mWindowAndroid;
93
94 // The webcontents observer on current content view of the tab.
95 private WebContentsObserver mCurrentViewWebContentsObserver;
96
97 // The webcontents observer that listens for events to swap in the new WebCo ntents.
98 private WebContentsObserver mWebContentsObserver;
99
100 // The webcontents delegate that gets notified when background view is ready to be swapped.
101 private final WebContentsDelegateAndroid mWebContentsDelegate;
102
103 // Whether or not the preview page finished loading.
104 private boolean mPreviewLoaded = false;
105
106 // Whether or not the background view has painted anything non-empty.
107 private boolean mPaintedNonEmpty = false;
108
109 private int mNumRendererFramesReceived = 0;
110
111 // Whether or not the swap is in progress. We will be in this state while sy ncing scroll offsets
112 // between content views.
113 private boolean mSwapInProgress = false;
114
115 // Whether or not the background view started loading content.
116 private boolean mDidStartLoad = false;
117
118 // Whether or not the background view finished loading.
119 private boolean mDidFinishLoad = false;
120
121 // The tab associated with the background view.
122 private final Tab mTab;
123
124 // The delegate who is notified when background content view is ready.
125 private final BackgroundContentViewDelegate mDelegate;
126
127 // GestureStateListener attached to the background contentView.
128 private final GestureStateListener mBackgroundGestureStateListener;
129
130 // GestureStateListener attached to the current contentView of tab.
131 private final GestureStateListener mCurrentViewGestureStateListener;
132
133 private final Handler mHandler;
134
135 private final Runnable mSwapOnTimeout = new Runnable() {
136 @Override
137 public void run() {
138 forceSwappingContentViews();
139 RecordHistogram.recordEnumeratedHistogram(
140 "InstantSearchClicks.ReasonForSwap",
141 SWAP_REASON_TIMEOUT, SWAP_REASON_BOUNDARY);
142 }
143 };
144
145 private long mBackgroundLoadStartTimeStampMs;
146
147 /**
148 * Whether or not to record 'instant search clicks' uma for the current load ed page in tab.
149 * This is set to true, for instant search clicks enabled urls(preview urls) and also for the
150 * 'would have been' instant search click enabled urls.
151 */
152 private boolean mRecordUma = false;
153
154 private final int mExperimentGroup;
155
156 /**
157 * The recent load progress before swapping. This will be used to return the progress after
158 * swapping the page views.
159 */
160 private int mLoadProgress;
161
162 /**
163 * Whether we transferred pagescale from preview to original.
164 */
165 private boolean mOriginalPageZoomed = false;
166
167 /**
168 * The delegate that is notified when BackgroundContentView is ready to be s wapped in.
169 */
170 public interface BackgroundContentViewDelegate {
171 /**
172 * Called when the background content view has drawn non-empty screen..
173 * @param cvc The background ContentViewCore that is ready to be swapped in.
174 * @param didStartLoad Whether WebContentsObserver::DidStartProvisional LoadForFrame() has
175 * been called for background ContentViewCore.
176 * @param didFinishLoad Whether WebContentsObserver::DidFinishLoad() has already been
177 * called for background {@link ContentViewCore}.
178 * @param progress The recent load progress that was received for t he
179 * {@link ContentView}.
180 */
181 void onBackgroundViewReady(
182 ContentViewCore cvc, boolean didStartLoad, boolean didFinishLoad , int progress);
183
184 /**
185 * Called to notify the load progress of backgrounc content view.
186 * @param progress The recent load progress that was received for t he
187 * {@link ContentView}.
188 */
189 void onLoadProgressChanged(int progress);
190 }
191
192 /**
193 * Creates an instance of BackgroundContentViewHelper.
194 * @param window An instance of a {@link WindowAndroid}.
195 * @param tab The {@link ChromeTab} associated with current backgruond view.
196 * @param delegate The {@link BackgroundContentViewDelegate} to notify when backgruond view is
197 * ready to swap in.
198 */
199 public BackgroundContentViewHelper(WindowAndroid windowAndroid, Tab tab,
200 BackgroundContentViewDelegate delegate) {
201 mWindowAndroid = windowAndroid;
202 mNativeBackgroundContentViewHelperPtr = nativeInit();
203
204 mTab = tab;
205 assert delegate != null;
206 mDelegate = delegate;
207 mTab.addObserver(this);
208 mBackgroundGestureStateListener = new GestureStateListener() {
209 @Override
210 public void onScrollOffsetOrExtentChanged(int scrollOffsetY, int scr ollExtentY) {
211 syncPageStateAndSwapIfReady();
212 }
213 };
214
215 mCurrentViewGestureStateListener = new GestureStateListener() {
216 @Override
217 public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) {
218 syncPageStateAndSwapIfReady();
219 }
220
221 @Override
222 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
223 syncPageStateAndSwapIfReady();
224 }
225
226 @Override
227 public void onSingleTap(boolean consumed, int x, int y) {
228 recordSingleTap();
229 }
230 };
231
232 mWebContentsDelegate = new WebContentsDelegateAndroid() {
233 @Override
234 public void onLoadProgressChanged(int progress) {
235 mLoadProgress = progress;
236 mDelegate.onLoadProgressChanged(progress);
237 trySwappingBackgroundView();
238 }
239 };
240 mHandler = new Handler();
241
242 mSwapTimeoutMs = nativeGetSwapTimeoutMs(mNativeBackgroundContentViewHelp erPtr);
243 mMinRendererFramesNeededForSwap =
244 nativeGetNumFramesNeededForSwap(mNativeBackgroundContentViewHelperPt r);
245 mBackgroundLoadStartTimeStampMs = System.currentTimeMillis();
246 mExperimentGroup = getExperimentGroup();
247 }
248
249 /*
250 * @return Experiment group that the current user fall into.
251 */
252 private int getExperimentGroup() {
253 if (!DeviceClassManager.enableInstantSearchClicks()) {
254 return INSTANT_SEARCH_CLICKS_EXPERIMENT_DISABLED;
255 }
256
257 String instantSearchClicksFieldTrialValue =
258 FieldTrialList.findFullName("InstantSearchClicks");
259 if (TextUtils.equals(instantSearchClicksFieldTrialValue, "Enabled")
260 || TextUtils.equals(instantSearchClicksFieldTrialValue,
261 "InstantSearchClicksEnabled")) {
262 return INSTANT_SEARCH_CLICKS_EXPERIMENT_ENABLED;
263 } else if (TextUtils.equals(instantSearchClicksFieldTrialValue, "Control ")) {
264 return INSTANT_SEARCH_CLICKS_EXPERIMENT_CONTROL;
265 }
266 return INSTANT_SEARCH_CLICKS_EXPERIMENT_DISABLED;
267 }
268
269 /**
270 * @return True iff content height of original page is greater than preview page.
271 */
272 private boolean hasBackgroundPageLoadedMoreThanPreview() {
273 if (!mPaintedNonEmpty || mTab.getContentViewCore() == null) return false ;
274 return getContentViewCore().getContentHeightCss()
275 > mTab.getContentViewCore().getContentHeightCss();
276 }
277
278 /**
279 * @return True iff original page is loading in background content view whic h is not swapped in.
280 */
281 public boolean hasPendingBackgroundPage() {
282 return mContentViewCore != null;
283 }
284
285 /**
286 * @return True iff swapping is in progress. This is true till the content v iew is completely
287 * swapped in.
288 */
289 public boolean isPageSwappingInProgress() {
290 return mSwapInProgress;
291 }
292
293 /**
294 * @return The {@link ContentViewCore} associated with the current backgroun d page.
295 */
296 public ContentViewCore getContentViewCore() {
297 return mContentViewCore;
298 }
299
300 /** @return The recent progress of background content view. */
301 public int getProgress() {
302 return mLoadProgress;
303 }
304
305 /**
306 * Loads the specified URL in the background content view.
307 * @param url the url to load.
308 */
309 private void loadUrl(String url) {
310 if (mNativeBackgroundContentViewHelperPtr == 0) return;
311
312 if (mContentViewCore == null) return;
313
314 mContentViewCore.onShow();
315
316 LoadUrlParams loadUrlParams = new LoadUrlParams(url);
317 // Use the referrer of preview url as referrer for original url.
318 // We are always loading url during onDidStartProvisionalLoadForFrame of preview URL, where
319 // load is not yet committed and so is the navigation entry. The current active
320 // navigation entry is still the referrer for preview url.
321 String referrerString = mTab.getWebContents()
322 .getNavigationController().getOriginalUrlForVisibleNavigationEnt ry();
323 Uri referrer = getUri(referrerString);
324 String referrerHost = referrer.getHost();
325 if (referrerHost == null
326 || TextUtils.indexOf(referrerHost, GOOGLE_DOMAIN_PREFIX) != 0) {
327 referrerHost = "www.google.com";
328 }
329 // We come here only through google.com and google.com always sends the referrer with
330 // value at least equal to the origin (and full url in http requests).
331 // Add 'gcjeid' param to referrer so that the PLT metrics are logged in a different bucket.
332 // PLT metrics are logged in a different bucket if the referer contains 'gcjeid' param.
333 // For Experiment: gcjeid=19
334 String referrerTrimmed = "http://" + referrerHost + "/?"
335 + GWS_CHROME_JOINT_EXPERIMENT_ID_STRING + "=19";
336 loadUrlParams.setReferrer(new Referrer(referrerTrimmed, 0 /* WebReferrer PolicyAlways */));
337 mContentViewCore.getWebContents().getNavigationController().loadUrl(load UrlParams);
338 }
339
340 /**
341 * Releases the ownership of the content view created. Also, resets the whol e state of the
342 * current object.
343 */
344 private ContentViewCore releaseContentViewCore() {
345 mTab.detachOverlayContentViewCore(mContentViewCore);
346
347 ContentViewCore originalContentViewCore = mContentViewCore;
348 if (mContentViewCore != null && mNativeBackgroundContentViewHelperPtr != 0) {
349 nativeReleaseWebContents(mNativeBackgroundContentViewHelperPtr);
350 }
351 reset();
352 return originalContentViewCore;
353 }
354
355 /**
356 * Destroys the contentview and the corresponding native object.
357 */
358 private void destroy() {
359 if (mCurrentViewWebContentsObserver != null) {
360 mCurrentViewWebContentsObserver.destroy();
361 mCurrentViewWebContentsObserver = null;
362 }
363 destroyContentViewCore();
364
365 if (mNativeBackgroundContentViewHelperPtr == 0) return;
366
367 nativeDestroy(mNativeBackgroundContentViewHelperPtr);
368 mNativeBackgroundContentViewHelperPtr = 0;
369 }
370
371 /**
372 * Creates a new {@link ContentViewCore} and loads the given url.
373 * @param url Url to load.
374 */
375 private void createContentViewCoreWithUrl(String url) {
376 if (url == null || mNativeBackgroundContentViewHelperPtr == 0) return;
377
378 destroyContentViewCore();
379
380 Activity activity = mWindowAndroid.getActivity().get();
381 mContentViewCore = new ContentViewCore(activity);
382 ContentView cv = new ContentView(activity, mContentViewCore);
383 mContentViewCore.initialize(cv, cv,
384 ContentViewUtil.createWebContents(mTab.isIncognito(), false), mW indowAndroid);
385
386 // The renderer should be set with the height considering the top contro ls(non-fullscreen
387 // mode).
388 mContentViewCore.setTopControlsHeight(mContentViewCore.getTopControlsHei ghtPix(), true);
389 mWebContentsObserver = new WebContentsObserver(mContentViewCore.getWebCo ntents()) {
390 @Override
391 public void didFirstVisuallyNonEmptyPaint() {
392 mPaintedNonEmpty = true;
393 trySwappingBackgroundView();
394 }
395 @Override
396 public void didStartProvisionalLoadForFrame(long frameId, long paren tFrameId,
397 boolean isMainFrame, String validatedUrl, boolean isErrorPag e,
398 boolean isIframeSrcdoc) {
399 if (isMainFrame) mDidStartLoad = true;
400 }
401
402 @Override
403 public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
404 if (isMainFrame) {
405 mDidFinishLoad = true;
406 trySwappingBackgroundView();
407 }
408 }
409
410 @Override
411 public void didFailLoad(boolean isProvisionalLoad,
412 boolean isMainFrame, int errorCode, String description, Stri ng failingUrl) {
413 if (isMainFrame) {
414 RecordHistogram.recordEnumeratedHistogram(
415 "InstantSearchClicks.ReasonForSwap",
416 SWAP_REASON_ORIGINAL_FAIL, SWAP_REASON_BOUNDARY);
417 mDidFinishLoad = true;
418 trySwappingBackgroundView();
419 }
420 }
421 };
422
423 mContentViewCore.setContentViewClient(new ContentViewClient() {
424 @Override
425 public void onOffsetsForFullscreenChanged(
426 float topControlsOffsetYPix,
427 float contentOffsetYPix,
428 float overdrawBottomHeightPix) {
429 // Using onOffsetsForFullscreenChanged, as it is called on every frame update.
430 // Here we know that we have an update to frame. Ideally we need a function like
431 // onUpdateFrame.
432 if (mPaintedNonEmpty
433 // We need to make sure the frame we got has some conten t. Sometimes
434 // didFirstVisuallyNonEmptyPaint is called, but still th e frame is empty and
435 // has the default width of Chrome '980'px. So waiting f or the contentWidth
436 // of both preview and original to become same. Ideally in all cases they
437 // should be same as they both are same content. Also if we get hung here
438 // for some pages in the worst cases we swap on full loa d of original, or
439 // our hard timeout will kick in.
440 && getContentViewCore().getContentWidthCss()
441 == mTab.getContentViewCore().getContentWidthCss( )
442 && !mSwapInProgress) {
443 mNumRendererFramesReceived++;
444 trySwappingBackgroundView();
445 }
446 }
447 });
448
449 if (mSwapTimeoutMs > 0) mHandler.postDelayed(mSwapOnTimeout, mSwapTimeou tMs);
450
451 nativeSetWebContents(mNativeBackgroundContentViewHelperPtr,
452 mContentViewCore, mWebContentsDelegate);
453
454 mBackgroundLoadStartTimeStampMs = System.currentTimeMillis();
455
456 loadUrl(url);
457 // Keep the toolbar always show in background view.
458 mContentViewCore.getWebContents().updateTopControlsState(false, true, fa lse);
459 mTab.attachOverlayContentViewCore(mContentViewCore, false);
460 }
461
462 /**
463 * Helper to delete {@link ContentViewCore} and it's native web contents obj ect.
464 */
465 private void destroyContentViewCore() {
466 if (mContentViewCore != null && mNativeBackgroundContentViewHelperPtr != 0) {
467 RecordHistogram.recordTimesHistogram(
468 "InstantSearchClicks.TimeInPreview",
469 System.currentTimeMillis() - mBackgroundLoadStartTimeStampMs ,
470 TimeUnit.MILLISECONDS);
471 mTab.detachOverlayContentViewCore(mContentViewCore);
472 mContentViewCore.destroy();
473 nativeDestroyWebContents(mNativeBackgroundContentViewHelperPtr);
474 }
475 reset();
476 // Note: We cannot set these in reset(), as we call reset() from release ContentView and
477 // these values are used after that.
478 mSwapInProgress = false;
479 mLoadProgress = 0;
480 mDidStartLoad = false;
481 mDidFinishLoad = false;
482 }
483
484 /**
485 * @return True iff Background view has finished loading or all the followin g conditions are
486 * met.
487 * 1. Preview page has loaded completely.
488 * 2. Background view has loaded atleast of preview page height.
489 * 3. Background view has painted anything non-empty.
490 */
491 private boolean isBackgroundViewReady() {
492 if (mDidFinishLoad) return true;
493
494 if (!mPreviewLoaded) return false;
495
496 if (!mPaintedNonEmpty) return false;
497
498 if (mNumRendererFramesReceived < mMinRendererFramesNeededForSwap) return false;
499
500 if (!hasBackgroundPageLoadedMoreThanPreview()) return false;
501
502 return true;
503 }
504
505 /** Called when any of the conditions needed for background view swap are me t. */
506 private void trySwappingBackgroundView() {
507 if (mSwapInProgress) return;
508 // This function is asynchronously called from various places.
509 // So make sure we have background content view before doing anything.
510 if (!hasPendingBackgroundPage()) return;
511 if (isBackgroundViewReady()) backgroundViewReady();
512 }
513
514 /** Called when we are ready to swap in background view to foreground. */
515 private void backgroundViewReady() {
516 if (mDelegate == null) return;
517 mSwapInProgress = true;
518 swapInBackgroundViewAfterScroll();
519 }
520
521 private boolean isGooglePreviewServerHost(String host) {
522 if (host == null) return false;
523 return TextUtils.equals(host, GOOGLE_PREVIEW_SERVER_PREFIX + GOOGLE_PREV IEW_SERVER_SUFFIX)
524 || (ChromeVersionInfo.isDevBuild() && host.endsWith(GOOGLE_PREVI EW_SERVER_SUFFIX)
525 && host.contains("promise"));
526 }
527
528 private Uri getUri(String url) {
529 return Uri.parse(url == null ? "" : url);
530 }
531
532 /**
533 * @return original url if the passed in url is a preview url, else null.
534 */
535 private String getOriginalUrlForPreviewUrl(String previewUrl) {
536 Uri previewUri = getUri(previewUrl);
537 String originalUrl = null;
538 if (isGooglePreviewServerHost(previewUri.getHost())) {
539 try {
540 originalUrl = (new URL(previewUri.getQueryParameter("url"))).toS tring();
541 } catch (MalformedURLException mue) {
542 originalUrl = null;
543 }
544 }
545 return originalUrl;
546 }
547
548
549 /**
550 * Checks if the incoming url is instant search clicks eligible url. Only ur ls with referer
551 * containing google search domain and 'gcjeid' param are instant search cli cks eligible url.
552 * @param url Url string.
553 */
554 private boolean isInstantSearchClicksControlUrl(String url) {
555 String referrer = mTab.getContentViewCore().getWebContents()
556 .getNavigationController().getOriginalUrlForVisibleNavigationEnt ry();
557 Uri referrerUri = getUri(referrer);
558 Uri previewUri = getUri(url);
559 if (referrerUri.getHost() == null || previewUri.getHost() == null) retur n false;
560 // Check if the url is coming from search page and has gcjeid param.
561 // TODO(ksimbili): Make this check stricter.
562 return (TextUtils.indexOf(referrerUri.getHost(), GOOGLE_DOMAIN_PREFIX) = = 0
563 && referrerUri.getQueryParameter(GWS_CHROME_JOINT_EXPERIMENT_ID_ STRING) != null
564 && TextUtils.indexOf(previewUri.getHost(), GOOGLE_DOMAIN_PREFIX) == -1);
565 }
566
567 private void setLogUma() {
568 // We ideally need to listen for gestures on current tab only while swap ping to detect when
569 // scrolling ends(if one is active). But for UMA logging, we are listeni ng for it always.
570 mTab.getContentViewCore().addGestureStateListener(mCurrentViewGestureSta teListener);
571 mRecordUma = true;
572 }
573
574 private void resetLogUma() {
575 if (mTab.getContentViewCore() != null) {
576 mTab.getContentViewCore().removeGestureStateListener(mCurrentViewGes tureStateListener);
577 }
578 mRecordUma = false;
579 }
580
581 /**
582 * Loads the original url in the background view, if the passed in url is a preview url.
583 * @param previewUrl Preview url string.
584 */
585 public void loadOriginalUrlIfPreview(String previewUrl) {
586 if (mExperimentGroup == INSTANT_SEARCH_CLICKS_EXPERIMENT_DISABLED) {
587 // If feature is disabled return;
588 return;
589 }
590
591 if (mExperimentGroup == INSTANT_SEARCH_CLICKS_EXPERIMENT_CONTROL
592 && isInstantSearchClicksControlUrl(previewUrl)) {
593 setLogUma();
594 return;
595 }
596
597 String originalUrl = getOriginalUrlForPreviewUrl(previewUrl);
598 if (originalUrl == null) return;
599
600 if (hasPendingBackgroundPage()) {
601 // The following condition is safe, as browser reload will always be on original url.
602 // Hence we will never encounter a situation where user is clicking on 'Reload' button
603 // and the page is not reloading.
604 if (originalUrl.equals(mContentViewCore.getWebContents().getUrl())) return;
605
606 // Log uma for the previous preview getting aborted
607 RecordHistogram.recordEnumeratedHistogram(
608 "InstantSearchClicks.ReasonForSwap",
609 SWAP_REASON_ABORT_ON_NAVIGATE, SWAP_REASON_BOUNDARY);
610 }
611
612 // Build content view.
613 createContentViewCoreWithUrl(originalUrl);
614
615 setLogUma();
616 }
617
618 /**
619 * Transfers zoom level from current cvc to the background cvc.
620 * @return Whether zoom level of background cvc had to be changed. It happen s only when it is
621 * different from current cvc.
622 */
623 private boolean transferPageScaleFactor() {
624 float currentViewPageScaleFactor =
625 mTab.getContentViewCore().getRenderCoordinates().getPageScaleFac tor();
626
627 ContentViewCore backgroundViewCore = getContentViewCore();
628 RenderCoordinates rc = backgroundViewCore.getRenderCoordinates();
629
630 // Clamp the input value.
631 float newPageScaleFactor = Math.max(rc.getMinPageScaleFactor(),
632 Math.min(currentViewPageScaleFactor, rc.getMaxPageScaleFactor()) );
633 if (newPageScaleFactor == rc.getPageScaleFactor()) return false;
634
635 float pageScaleFactorDelta = (newPageScaleFactor / rc.getPageScaleFactor ());
636 backgroundViewCore.pinchByDelta(pageScaleFactorDelta);
637 mOriginalPageZoomed = true;
638 return true;
639 }
640
641 /**
642 * Transfers scroll offset from current cvc to the background cvc.
643 * @return Whether scroll offset of background cvc had to be changed. It hap pens only when it
644 * is different from current cvc.
645 */
646 private boolean transferScrollOffset() {
647 ContentViewCore currentViewCore = mTab.getContentViewCore();
648 RenderCoordinates currentViewRenderCoordinates = currentViewCore.getRend erCoordinates();
649 float currentViewScrollX = currentViewRenderCoordinates.getScrollXPix();
650 float currentViewScrollY = currentViewRenderCoordinates.getScrollYPix();
651
652 ContentViewCore backgroundViewCore = getContentViewCore();
653 RenderCoordinates backgroundViewRenderCoordinates =
654 backgroundViewCore.getRenderCoordinates();
655 float maxHorizontalScroll = backgroundViewRenderCoordinates.getMaxHorizo ntalScrollPix();
656 float maxVerticalScroll = backgroundViewRenderCoordinates.getMaxVertical ScrollPix();
657
658 // Bound the scroll offsets to max scroll values.
659 float newScrollX = Math.min(currentViewScrollX, maxHorizontalScroll);
660 float newScrollY = Math.min(currentViewScrollY, maxVerticalScroll);
661
662 float backgroundViewScrollX = backgroundViewRenderCoordinates.getScrollX Pix();
663 float backgroundViewScrollY = backgroundViewRenderCoordinates.getScrollY Pix();
664 int scrollByX = Math.round(backgroundViewRenderCoordinates.fromPixToLoca lCss(
665 newScrollX - backgroundViewScrollX));
666 int scrollByY = Math.round(backgroundViewRenderCoordinates.fromPixToLoca lCss(
667 newScrollY - backgroundViewScrollY));
668
669 if (Math.abs(scrollByX) <= 1 && Math.abs(scrollByY) <= 1) return false;
670
671 if (!mOriginalPageZoomed) {
672 backgroundViewCore.scrollBy(Math.round(newScrollX - backgroundViewSc rollX),
673 Math.round(newScrollY - backgroundViewScrollY));
674 } else {
675 backgroundViewCore.scrollTo(Math.round(newScrollX), Math.round(newSc rollY));
676 }
677 return true;
678 }
679
680 private void swapInBackgroundViewAfterScroll() {
681 getContentViewCore().addGestureStateListener(mBackgroundGestureStateList ener);
682 syncPageStateAndSwapIfReady();
683 }
684
685 // Set scroll offset and zoom values from current cvc and swap the cvc if th e both are synced.
686 private void syncPageStateAndSwapIfReady() {
687 // This function is asynchronously called from various places.
688 // So make sure we have background content view before doing anything.
689 if (!hasPendingBackgroundPage() || !isPageSwappingInProgress()) return;
690
691 ContentViewCore currentViewCore = mTab.getContentViewCore();
692 if (currentViewCore == null || currentViewCore.isScrollInProgress()) {
693 // We'll comeback here on scrollEnded and flingEndGesture.
694 return;
695 }
696
697 if (transferPageScaleFactor()) return;
698
699 if (transferScrollOffset()) return;
700
701 RenderCoordinates currentViewRenderCoordinates = currentViewCore.getRend erCoordinates();
702 int currentViewScrollY = currentViewRenderCoordinates.getScrollYPixInt() ;
703
704 int state;
705 if (currentViewScrollY == 0) {
706 state = PREVIEW_SCROLL_STATE_NO_SCROLL;
707 } else if (currentViewScrollY >= currentViewRenderCoordinates.getMaxVert icalScrollPix()) {
708 state = PREVIEW_SCROLL_STATE_SCROLL_AT_BOTTOM;
709 } else {
710 state = PREVIEW_SCROLL_STATE_SCROLL;
711 }
712 RecordHistogram.recordEnumeratedHistogram(
713 "InstantSearchClicks.PreviewScrollState", state, PREVIEW_SCROLL_ STATE_BOUNDARY);
714 RecordHistogram.recordEnumeratedHistogram(
715 "InstantSearchClicks.ReasonForSwap", SWAP_REASON_REGULAR, SWAP_R EASON_BOUNDARY);
716
717 // We are ready swap the view now.
718 swapInBackgroundView();
719 }
720
721 private void swapInBackgroundView() {
722 // This function is asynchronously called from various places.
723 // So make sure we have background content view before doing anything.
724 if (!hasPendingBackgroundPage()) return;
725
726 if (mTab.getContentViewCore() != null) {
727 mTab.getContentViewCore().removeGestureStateListener(mCurrentViewGes tureStateListener);
728 }
729 getContentViewCore().removeGestureStateListener(mBackgroundGestureStateL istener);
730 assert mDelegate != null;
731
732 // Make sure the ContentViewCore is visible.
733 getContentViewCore().setDrawsContent(true);
734
735 // Merge history stack before swap.
736 nativeMergeHistoryFrom(mNativeBackgroundContentViewHelperPtr, mTab.getWe bContents());
737 ContentViewCore cvc = releaseContentViewCore();
738 mDelegate.onBackgroundViewReady(cvc, mDidStartLoad, mDidFinishLoad, mLoa dProgress);
739
740 RecordHistogram.recordTimesHistogram("InstantSearchClicks.TimeToSwap",
741 System.currentTimeMillis() - mBackgroundLoadStartTimeStampMs,
742 TimeUnit.MILLISECONDS);
743
744 // Content view is released already, but still calling this to clear som e state.
745 destroyContentViewCore();
746
747 // We need to include metrics from orignal page after swap too. For exam ple, single tap
748 // metric should include taps in both preview and original page after sw ap. This we reset
749 // when tab commits a new navigation.
750 setLogUma();
751 }
752
753 /** Stop the current navigation. */
754 public void stopLoading() {
755 destroyContentViewCore();
756 }
757
758 /**
759 * Forces swapping of content views.
760 */
761 public void forceSwappingContentViews() {
762 RecordHistogram.recordEnumeratedHistogram(
763 "InstantSearchClicks.ReasonForSwap", SWAP_REASON_FORCE, SWAP_REA SON_BOUNDARY);
764 swapInBackgroundView();
765 }
766
767 /**
768 * Calls 'unload' handler and deletes the webContents.
769 * @param webContents The webcontents to be deleted.
770 */
771 public void unloadAndDeleteWebContents(WebContents webContents) {
772 nativeUnloadAndDeleteWebContents(mNativeBackgroundContentViewHelperPtr, webContents);
773 }
774
775 /**
776 * Helper to reset the state of the object.
777 */
778 private void reset() {
779 mContentViewCore = null;
780 if (mWebContentsObserver != null) {
781 mWebContentsObserver.destroy();
782 mWebContentsObserver = null;
783 }
784
785 mPreviewLoaded = false;
786 mPaintedNonEmpty = false;
787 mNumRendererFramesReceived = 0;
788 resetLogUma();
789 mHandler.removeCallbacks(mSwapOnTimeout);
790 mOriginalPageZoomed = false;
791 }
792
793 @Override
794 public void onLoadProgressChanged(Tab tab, int progress) {
795 if (!hasPendingBackgroundPage()) return;
796 if (progress >= 100) {
797 mPreviewLoaded = true;
798 trySwappingBackgroundView();
799 }
800 }
801
802 @Override
803 public void onContentChanged(Tab tab) {
804 if (mCurrentViewWebContentsObserver != null) {
805 mCurrentViewWebContentsObserver.destroy();
806 }
807 if (tab.getContentViewCore() == null) return;
808
809 loadOriginalUrlIfPreview(mTab.getUrl());
810
811 mCurrentViewWebContentsObserver = new WebContentsObserver(mTab.getWebCon tents()) {
812 @Override
813 public void didFailLoad(boolean isProvisionalLoad,
814 boolean isMainFrame, int errorCode, String description, Stri ng failingUrl) {
815 if (isMainFrame && hasPendingBackgroundPage()) {
816 if (TextUtils.equals(mContentViewCore.getWebContents().getUr l(),
817 getOriginalUrlForPreviewUrl(failingUrl))) {
818 forceSwappingContentViews();
819 RecordHistogram.recordEnumeratedHistogram(
820 "InstantSearchClicks.ReasonForSwap",
821 SWAP_REASON_ON_PREVIEW_FAIL, SWAP_REASON_BOUNDAR Y);
822 }
823 }
824 }
825
826 @Override
827 public void didStartProvisionalLoadForFrame(long frameId, long paren tFrameId,
828 boolean isMainFrame, String validatedUrl, boolean isErrorPag e,
829 boolean isIframeSrcdoc) {
830 if (isMainFrame) {
831 if (mTab.getContentViewCore() != null) {
832 loadOriginalUrlIfPreview(validatedUrl);
833 } else {
834 destroyContentViewCore();
835 }
836 }
837 }
838
839 @Override
840 public void didCommitProvisionalLoadForFrame(
841 long frameId, boolean isMainFrame, String url, int transitio nType) {
842 if (!isMainFrame) return;
843
844 if (!hasPendingBackgroundPage() && !isInstantSearchClicksControl Url(url)) {
845 resetLogUma();
846 }
847
848 if (hasPendingBackgroundPage() && getOriginalUrlForPreviewUrl(ur l) == null) {
849 // We don't destroy the contentView in onDidStartProvisional LoadForFrame, if the
850 // url is navigating to non-preview URL. We instead do that here. Some urls
851 // which are handled using intents, the url is never commite d. In which case we
852 // still want to swap to happen.
853 destroyContentViewCore();
854 }
855 }
856 };
857 }
858
859 @Override
860 public void onDestroyed(Tab tab) {
861 destroy();
862 }
863
864 @CalledByNative
865 private long getNativePtr() {
866 return mNativeBackgroundContentViewHelperPtr;
867 }
868
869 public void recordBack() {
870 if (mRecordUma) RecordUserAction.record("MobilePreviewPageBack");
871 }
872
873 public void recordReload() {
874 if (mRecordUma) RecordUserAction.record("MobilePreviewPageReload");
875 }
876
877 public void recordTabClose() {
878 if (mRecordUma) RecordUserAction.record("MobilePreviewPageTabClose");
879 }
880
881 public void recordSingleTap() {
882 if (mRecordUma) RecordUserAction.record("MobilePreviewPageSingleTap");
883 }
884
885 private native long nativeInit();
886 private native void nativeDestroy(long nativeBackgroundContentViewHelper);
887 private native void nativeSetWebContents(long nativeBackgroundContentViewHel per,
888 ContentViewCore contentViewCore, WebContentsDelegateAndroid delegate );
889 private native void nativeDestroyWebContents(long nativeBackgroundContentVie wHelper);
890 private native void nativeReleaseWebContents(long nativeBackgroundContentVie wHelper);
891 private native void nativeMergeHistoryFrom(long nativeBackgroundContentViewH elper,
892 WebContents webContents);
893 private native void nativeUnloadAndDeleteWebContents(long nativeBackgroundCo ntentViewHelper,
894 WebContents webContents);
895 private native int nativeGetSwapTimeoutMs(long nativeBackgroundContentViewHe lper);
896 private native int nativeGetNumFramesNeededForSwap(long nativeBackgroundCont entViewHelper);
897 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698