Commit 864dcb75 authored by Patrick Noland's avatar Patrick Noland Committed by Commit Bot

[ToolbarMVC] Make LoadProgress into a discrete MVC Component

This moves logic from ToolbarManager into a Coordinator, Mediator, and
ViewBinder that are hooked up to the existing progress view, and adds
unit tests for the new mediator.
This change shouldn't affect visible behavior.

Bug: 865801
Change-Id: I24e121aec7be79535b67d8c5b647327c0e8ba99c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2001264Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Commit-Queue: Patrick Noland <pnoland@chromium.org>
Cr-Commit-Position: refs/heads/master@{#745075}
parent fba9a5b4
......@@ -1695,6 +1695,11 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarMediator.java",
"java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarModel.java",
"java/src/org/chromium/chrome/browser/toolbar/bottom/TabSwitcherBottomToolbarViewBinder.java",
"java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressCoordinator.java",
"java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediator.java",
"java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressProperties.java",
"java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressSimulator.java",
"java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressViewBinder.java",
"java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java",
"java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java",
"java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbarAnimationDelegate.java",
......
......@@ -206,6 +206,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/tabstate/TabStateUnitTest.java",
"junit/src/org/chromium/chrome/browser/tasks/EngagementTimeUtilTest.java",
"junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java",
"junit/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediatorTest.java",
"junit/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarMediatorUnitTest.java",
"junit/src/org/chromium/chrome/browser/usage_stats/EventTrackerTest.java",
"junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java",
......
......@@ -9,8 +9,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
......@@ -26,7 +24,6 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Callback;
import org.chromium.base.MathUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.ObservableSupplier;
......@@ -57,7 +54,6 @@ import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
import org.chromium.chrome.browser.metrics.OmniboxStartupMetrics;
import org.chromium.chrome.browser.native_page.NativePageFactory;
import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
import org.chromium.chrome.browser.ntp.FakeboxDelegate;
import org.chromium.chrome.browser.ntp.IncognitoNewTabPage;
......@@ -95,6 +91,7 @@ import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
import org.chromium.chrome.browser.toolbar.bottom.BottomTabSwitcherActionMenuCoordinator;
import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarConfiguration;
import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressCoordinator;
import org.chromium.chrome.browser.toolbar.top.ActionModeController;
import org.chromium.chrome.browser.toolbar.top.ActionModeController.ActionBarDelegate;
import org.chromium.chrome.browser.toolbar.top.TabSwitcherActionMenuCoordinator;
......@@ -161,12 +158,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
*/
private static final int RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS = 30000;
/**
* The minimum load progress that can be shown when a page is loading. This is not 0 so that
* it's obvious to the user that something is attempting to load.
*/
private static final float MINIMUM_LOAD_PROGRESS = 0.05f;
private final IncognitoStateProvider mIncognitoStateProvider;
private final TabCountProvider mTabCountProvider;
private final ThemeColorProvider mTabThemeColorProvider;
......@@ -205,13 +196,13 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
private final ActionBarDelegate mActionBarDelegate;
private ActionModeController mActionModeController;
private final ToolbarActionModeCallback mToolbarActionModeCallback;
private LoadProgressSimulator mLoadProgressSimulator;
private final Callback<Boolean> mUrlFocusChangedCallback;
private final Handler mHandler = new Handler();
private final ChromeActivity mActivity;
private final ChromeFullscreenManager mFullscreenManager;
private UrlFocusChangeListener mLocationBarFocusObserver;
private ComponentCallbacks mComponentCallbacks;
private final LoadProgressCoordinator mProgressBarCoordinator;
private BrowserStateBrowserControlsVisibilityDelegate mControlsVisibilityDelegate;
private int mFullscreenFocusToken = TokenHolder.INVALID_TOKEN;
......@@ -374,6 +365,8 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
mLocationBar.initializeControls(new WindowDelegate(mActivity.getWindow()),
mActivity.getWindowAndroid(), mActivity.getActivityTabProvider());
mLocationBar.addUrlFocusChangeListener(mLocationBarFocusObserver);
mProgressBarCoordinator = new LoadProgressCoordinator(
mActivity.getActivityTabProvider(), mToolbar.getProgressBar());
mToolbar.initialize(mLocationBarModel, this);
mToolbar.addUrlExpansionObserver(activity.getStatusBarColorController());
......@@ -463,7 +456,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
public void onCrash(Tab tab) {
updateTabLoadingState(false);
updateButtonStatus();
finishLoadProgress(false);
}
@Override
......@@ -492,22 +484,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
public void onLoadStopped(Tab tab, boolean toDifferentDocument) {
if (!toDifferentDocument) return;
updateTabLoadingState(true);
// If we made some progress, fast-forward to complete, otherwise just dismiss any
// MINIMUM_LOAD_PROGRESS that had been set.
if (tab.getProgress() > MINIMUM_LOAD_PROGRESS && tab.getProgress() < 1) {
updateLoadProgress(1);
}
finishLoadProgress(true);
}
@Override
public void onLoadProgressChanged(Tab tab, float progress) {
if (NativePageFactory.isNativePageUrl(tab.getUrlString(), tab.isIncognito())) {
return;
}
updateLoadProgress(progress);
}
@Override
......@@ -523,9 +499,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
if (!didStartLoad) return;
mLocationBar.updateLoadingState(true);
if (didFinishLoad) {
mLoadProgressSimulator.start();
}
}
@Override
......@@ -578,22 +551,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
&& tab.getWebContents().getNavigationController().isInitialNavigation()) {
mLocationBar.setUrlToPageUrl();
}
if (navigation.isSameDocument()) return;
// This event is used as the primary trigger for the progress bar because it
// is the earliest indication that a load has started for a particular frame. In
// the case of the progress bar, it should only traverse the screen a single time
// per page load. So if this event states the main frame has started loading the
// progress bar is started.
if (NativePageFactory.isNativePageUrl(navigation.getUrl(), tab.isIncognito())) {
finishLoadProgress(false);
return;
}
mLoadProgressSimulator.cancel();
startLoadProgress();
updateLoadProgress(tab.getProgress());
}
@Override
......@@ -623,7 +580,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
ntp.setUrlFocusAnimationsDisabled(false);
mToolbar.onTabOrModelChanged();
if (mToolbar.getProgressBar() != null) mToolbar.getProgressBar().finish(false);
}
}
......@@ -756,8 +712,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
}
};
mLoadProgressSimulator = new LoadProgressSimulator(this);
mToolbar.setTabCountProvider(mTabCountProvider);
mToolbar.setIncognitoStateProvider(mIncognitoStateProvider);
mToolbar.setThemeColorProvider(
......@@ -1856,28 +1810,8 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
}
private void updateCurrentTabDisplayStatus() {
Tab tab = mLocationBarModel.getTab();
mLocationBar.setUrlToPageUrl();
updateTabLoadingState(true);
if (tab == null) {
finishLoadProgress(false);
return;
}
mLoadProgressSimulator.cancel();
if (tab.isLoading()) {
if (NativePageFactory.isNativePageUrl(tab.getUrlString(), tab.isIncognito())) {
finishLoadProgress(false);
} else {
startLoadProgress();
updateLoadProgress(tab.getProgress());
}
} else {
finishLoadProgress(false);
}
}
private void updateTabLoadingState(boolean updateUrl) {
......@@ -1885,34 +1819,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
if (updateUrl) updateButtonStatus();
}
private void updateLoadProgress(float progress) {
// If it's a native page, progress bar is already hidden or being hidden, so don't update
// the value.
// TODO(kkimlabs): Investigate back/forward navigation with native page & web content and
// figure out the correct progress bar presentation.
Tab tab = mLocationBarModel.getTab();
if (tab == null
|| NativePageFactory.isNativePageUrl(tab.getUrlString(), tab.isIncognito())) {
return;
}
progress = Math.max(progress, MINIMUM_LOAD_PROGRESS);
mToolbar.setLoadProgress(progress);
if (MathUtils.areFloatsEqual(progress, 1)) finishLoadProgress(true);
}
private void finishLoadProgress(boolean delayed) {
mLoadProgressSimulator.cancel();
mToolbar.finishLoadProgress(delayed);
}
/**
* Only start showing the progress bar if it is not already started.
*/
private void startLoadProgress() {
if (mToolbar.isProgressStarted()) return;
mToolbar.startLoadProgress();
}
/**
* @param enabled Whether the progress bar is enabled.
......@@ -1921,9 +1827,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
mToolbar.setProgressBarEnabled(enabled);
}
/**
* @param anchor The view to use as an anchor.
*/
public void setProgressBarAnchorView(@Nullable View anchor) {
mToolbar.setProgressBarAnchorView(anchor);
}
......@@ -1964,53 +1867,6 @@ public class ToolbarManager implements ScrimObserver, ToolbarTabController, UrlF
mControlContainer.setLayoutParams(layoutParams);
}
private static class LoadProgressSimulator {
private static final int MSG_ID_UPDATE_PROGRESS = 1;
private static final float PROGRESS_INCREMENT = 0.1f;
private static final int PROGRESS_INCREMENT_DELAY_MS = 10;
private final ToolbarManager mToolbarManager;
private final Handler mHandler;
private float mProgress;
public LoadProgressSimulator(ToolbarManager toolbar) {
mToolbarManager = toolbar;
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
assert msg.what == MSG_ID_UPDATE_PROGRESS;
mProgress = Math.min(1, mProgress += PROGRESS_INCREMENT);
mToolbarManager.updateLoadProgress(mProgress);
if (MathUtils.areFloatsEqual(mProgress, 1)) {
mToolbarManager.mToolbar.finishLoadProgress(true);
return;
}
sendEmptyMessageDelayed(MSG_ID_UPDATE_PROGRESS, PROGRESS_INCREMENT_DELAY_MS);
}
};
}
/**
* Start simulating load progress from a baseline of 0.
*/
public void start() {
mProgress = 0;
mToolbarManager.mToolbar.startLoadProgress();
mToolbarManager.updateLoadProgress(mProgress);
mHandler.sendEmptyMessage(MSG_ID_UPDATE_PROGRESS);
}
/**
* Cancels simulating load progress.
*/
public void cancel() {
mHandler.removeMessages(MSG_ID_UPDATE_PROGRESS);
}
}
/** Return the location bar model for testing purposes. */
@VisibleForTesting
public LocationBarModel getLocationBarModelForTesting() {
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar.load_progress;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
/**
* Coordinator for the load progress bar. Owns all progress bar sub-components.
*/
public class LoadProgressCoordinator {
private final PropertyModel mModel;
private final LoadProgressMediator mMediator;
private final ToolbarProgressBar mProgressBarView;
private final LoadProgressViewBinder mLoadProgressViewBinder;
private final PropertyModelChangeProcessor<PropertyModel, ToolbarProgressBar, PropertyKey>
mPropertyModelChangeProcessor;
public LoadProgressCoordinator(
ActivityTabProvider activityTabProvider, ToolbarProgressBar progressBarView) {
mProgressBarView = progressBarView;
mModel = new PropertyModel(LoadProgressProperties.ALL_KEYS);
mMediator = new LoadProgressMediator(activityTabProvider, mModel);
mLoadProgressViewBinder = new LoadProgressViewBinder();
mPropertyModelChangeProcessor = PropertyModelChangeProcessor.create(
mModel, mProgressBarView, mLoadProgressViewBinder::bind);
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar.load_progress;
import org.chromium.base.MathUtils;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.native_page.NativePageFactory;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressProperties.CompletionState;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.ui.modelutil.PropertyModel;
/**
* Mediator for the load progress bar. Listens for changes to the loading state of the current tab
* and adjusts its property model accordingly.
*/
public class LoadProgressMediator {
static final float MINIMUM_LOAD_PROGRESS = 0.05f;
private final PropertyModel mModel;
private final EmptyTabObserver mTabObserver;
private final LoadProgressSimulator mLoadProgressSimulator;
public LoadProgressMediator(ActivityTabProvider activityTabProvider, PropertyModel model) {
mModel = model;
mLoadProgressSimulator = new LoadProgressSimulator(model);
mTabObserver = new ActivityTabProvider.ActivityTabTabObserver(activityTabProvider) {
@Override
public void onDidStartNavigation(Tab tab, NavigationHandle navigation) {
if (!navigation.isInMainFrame()) {
return;
}
if (NativePageFactory.isNativePageUrl(navigation.getUrl(), tab.isIncognito())) {
finishLoadProgress(false);
return;
}
mLoadProgressSimulator.cancel();
startLoadProgress();
updateLoadProgress(tab.getProgress());
}
@Override
public void onLoadStopped(Tab tab, boolean toDifferentDocument) {
if (!toDifferentDocument) return;
// If we made some progress, fast-forward to complete, otherwise just dismiss any
// MINIMUM_LOAD_PROGRESS that had been set.
if (tab.getProgress() > MINIMUM_LOAD_PROGRESS && tab.getProgress() < 1) {
updateLoadProgress(1.0f);
}
finishLoadProgress(true);
}
@Override
public void onLoadProgressChanged(Tab tab, float progress) {
if (NewTabPage.isNTPUrl(tab.getUrlString())
|| NativePageFactory.isNativePageUrl(
tab.getUrlString(), tab.isIncognito())) {
return;
}
updateLoadProgress(progress);
}
@Override
public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
// If loading both started and finished before we swapped in the WebContents, we
// won't get any load progress signals. Otherwise, we should receive at least one
// real signal so we don't need to simulate them.
if (didStartLoad && didFinishLoad) {
mLoadProgressSimulator.start();
}
}
@Override
public void onCrash(Tab tab) {
finishLoadProgress(false);
}
@Override
protected void onObservingDifferentTab(Tab tab) {
onNewTabObserved(tab);
}
};
onNewTabObserved(activityTabProvider.get());
}
private void onNewTabObserved(Tab tab) {
if (tab == null) return;
if (tab.isLoading()) {
if (NativePageFactory.isNativePageUrl(tab.getUrlString(), tab.isIncognito())) {
finishLoadProgress(false);
} else {
startLoadProgress();
updateLoadProgress(tab.getProgress());
}
} else {
finishLoadProgress(false);
}
}
private void startLoadProgress() {
mModel.set(LoadProgressProperties.COMPLETION_STATE, CompletionState.UNFINISHED);
}
private void updateLoadProgress(float progress) {
progress = Math.max(progress, MINIMUM_LOAD_PROGRESS);
mModel.set(LoadProgressProperties.PROGRESS, progress);
if (MathUtils.areFloatsEqual(progress, 1)) finishLoadProgress(true);
}
private void finishLoadProgress(boolean animateCompletion) {
mLoadProgressSimulator.cancel();
@CompletionState
int completionState = animateCompletion ? CompletionState.FINISHED_DO_ANIMATE
: CompletionState.FINISHED_DONT_ANIMATE;
mModel.set(LoadProgressProperties.COMPLETION_STATE, completionState);
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar.load_progress;
import androidx.annotation.IntDef;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** List of load progress bar properties. */
class LoadProgressProperties {
@IntDef({
CompletionState.UNFINISHED,
CompletionState.FINISHED_DO_ANIMATE,
CompletionState.FINISHED_DONT_ANIMATE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CompletionState {
int UNFINISHED = 0;
int FINISHED_DO_ANIMATE = 1;
int FINISHED_DONT_ANIMATE = 2;
}
public static final PropertyModel.WritableIntPropertyKey COMPLETION_STATE =
new PropertyModel.WritableIntPropertyKey();
public static final PropertyModel.WritableFloatPropertyKey PROGRESS =
new PropertyModel.WritableFloatPropertyKey();
static final PropertyKey[] ALL_KEYS = {COMPLETION_STATE, PROGRESS};
}
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar.load_progress;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.MathUtils;
import org.chromium.ui.modelutil.PropertyModel;
/**
* Simulator for load progress changes when the page being rendered doesn't actually send load
* progress signals, e.g. when swapping in a pre-rendered page. Uses a message loop to send update
* messages to itself to update the simulated progress value on a regular interval.
*/
class LoadProgressSimulator {
private static final int MSG_ID_UPDATE_PROGRESS = 1;
private static final int PROGRESS_INCREMENT_DELAY_MS = 10;
@VisibleForTesting
static final float PROGRESS_INCREMENT = 0.1f;
private final PropertyModel mModel;
private final Handler mHandler;
private float mProgress;
public LoadProgressSimulator(PropertyModel model) {
mModel = model;
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
assert msg.what == MSG_ID_UPDATE_PROGRESS;
mProgress = Math.min(1, mProgress += PROGRESS_INCREMENT);
mModel.set(LoadProgressProperties.PROGRESS, mProgress);
if (MathUtils.areFloatsEqual(mProgress, 1.0f)) {
mModel.set(LoadProgressProperties.COMPLETION_STATE,
LoadProgressProperties.CompletionState.FINISHED_DO_ANIMATE);
return;
}
sendEmptyMessageDelayed(MSG_ID_UPDATE_PROGRESS, PROGRESS_INCREMENT_DELAY_MS);
}
};
}
/**
* Start simulating load progress from a baseline of 0.
*/
public void start() {
mProgress = 0.0f;
mModel.set(LoadProgressProperties.COMPLETION_STATE,
LoadProgressProperties.CompletionState.UNFINISHED);
mModel.set(LoadProgressProperties.PROGRESS, mProgress);
mHandler.sendEmptyMessage(MSG_ID_UPDATE_PROGRESS);
}
/**
* Cancels simulating load progress.
*/
public void cancel() {
mModel.set(LoadProgressProperties.COMPLETION_STATE,
LoadProgressProperties.CompletionState.FINISHED_DONT_ANIMATE);
mHandler.removeMessages(MSG_ID_UPDATE_PROGRESS);
}
}
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar.load_progress;
import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressProperties.CompletionState;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
/**
* View binder for the load progress bar. Adjusts view properties on ToolbarProgressBar in response
* to changes in the associated property model.
*/
public class LoadProgressViewBinder {
public void bind(PropertyModel model, ToolbarProgressBar view, PropertyKey propertyKey) {
if (propertyKey == LoadProgressProperties.COMPLETION_STATE) {
@CompletionState
int completionState = model.get(LoadProgressProperties.COMPLETION_STATE);
boolean done = !(completionState == CompletionState.UNFINISHED);
if (done) {
view.finish(completionState == CompletionState.FINISHED_DO_ANIMATE);
} else {
view.start();
}
} else if (propertyKey == LoadProgressProperties.PROGRESS) {
float progress = model.get(LoadProgressProperties.PROGRESS);
view.setProgress(progress);
}
}
}
mdjones@chromium.org
pnoland@chromium.org
# COMPONENT: UI>Browser>Toolbar
# OS: Android
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar.load_progress;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Looper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.chromium.base.Callback;
import org.chromium.base.MathUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabImpl;
import org.chromium.chrome.browser.tab.TabObserver;
import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressProperties.CompletionState;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.ui.modelutil.PropertyModel;
/** Unit tests for LoadProgressMediator. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class LoadProgressMediatorTest {
private static final String URL_1 = "http://starting.url";
private static final String NATIVE_PAGE_URL = "chrome-native://newtab";
@Mock
public ActivityTabProvider mActivityTabProvider;
@Mock
private TabImpl mTab;
@Mock
private TabImpl mTab2;
@Captor
public ArgumentCaptor<TabObserver> mTabObserverCaptor;
@Captor
public ArgumentCaptor<Callback<Tab>> mActivityTabObserverCaptor;
private PropertyModel mModel;
private LoadProgressMediator mMediator;
private TabObserver mTabObserver;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mActivityTabProvider.get()).thenReturn(mTab);
mModel = new PropertyModel(LoadProgressProperties.ALL_KEYS);
mMediator = new LoadProgressMediator(mActivityTabProvider, mModel);
verify(mActivityTabProvider).addObserver(mActivityTabObserverCaptor.capture());
verify(mTab).addObserver(mTabObserverCaptor.capture());
mTabObserver = mTabObserverCaptor.getValue();
}
@Test
public void loadRegularPage() {
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DONT_ANIMATE);
NavigationHandle navigation = new NavigationHandle(0, URL_1, true, false, false);
mTabObserver.onDidStartNavigation(mTab, navigation);
assertEquals(
mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS),
LoadProgressMediator.MINIMUM_LOAD_PROGRESS, MathUtils.EPSILON);
mTabObserver.onLoadProgressChanged(mTab, 0.1f);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS), 0.1f, MathUtils.EPSILON);
mTabObserver.onLoadProgressChanged(mTab, 1.0f);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS), 1.0f, MathUtils.EPSILON);
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DO_ANIMATE);
}
@Test
public void switchToLoadingTab() {
doReturn(true).when(mTab2).isLoading();
doReturn(0.1f).when(mTab2).getProgress();
mActivityTabObserverCaptor.getValue().onResult(mTab2);
verify(mTab2, times(1)).addObserver(any());
assertEquals(
mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS), 0.1f, MathUtils.EPSILON);
}
@Test
public void switchToLoadedTab() {
NavigationHandle navigation = new NavigationHandle(0, URL_1, true, false, false);
mTabObserver.onDidStartNavigation(mTab, navigation);
assertEquals(
mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS),
LoadProgressMediator.MINIMUM_LOAD_PROGRESS, MathUtils.EPSILON);
mActivityTabObserverCaptor.getValue().onResult(mTab2);
verify(mTab2, times(1)).addObserver(any());
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DONT_ANIMATE);
}
@Test
public void loadNativePage() {
doReturn(0.1f).when(mTab).getProgress();
NavigationHandle navigation = new NavigationHandle(0, URL_1, true, false, false);
mTabObserver.onDidStartNavigation(mTab, navigation);
assertEquals(
mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS), 0.1f, MathUtils.EPSILON);
navigation = new NavigationHandle(0, NATIVE_PAGE_URL, true, false, false);
mTabObserver.onDidStartNavigation(mTab, navigation);
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DONT_ANIMATE);
}
@Test
public void switchToTabWithNativePage() {
NavigationHandle navigation = new NavigationHandle(0, URL_1, true, false, false);
mTabObserver.onDidStartNavigation(mTab, navigation);
assertEquals(
mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS),
LoadProgressMediator.MINIMUM_LOAD_PROGRESS, MathUtils.EPSILON);
when(mTab2.getUrlString()).thenReturn(NATIVE_PAGE_URL);
mActivityTabObserverCaptor.getValue().onResult(mTab2);
verify(mTab2, times(1)).addObserver(any());
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DONT_ANIMATE);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS),
LoadProgressMediator.MINIMUM_LOAD_PROGRESS, MathUtils.EPSILON);
}
@Test
public void pageCrashes() {
NavigationHandle navigation = new NavigationHandle(0, URL_1, true, false, false);
mTabObserver.onDidStartNavigation(mTab, navigation);
assertEquals(
mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS),
LoadProgressMediator.MINIMUM_LOAD_PROGRESS, MathUtils.EPSILON);
mTabObserver.onCrash(mTab);
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DONT_ANIMATE);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS),
LoadProgressMediator.MINIMUM_LOAD_PROGRESS, MathUtils.EPSILON);
}
@Test
public void testSwapWebContents() {
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DONT_ANIMATE);
mTabObserver.onWebContentsSwapped(mTab, true, true);
assertEquals(
mModel.get(LoadProgressProperties.COMPLETION_STATE), CompletionState.UNFINISHED);
assertEquals(mModel.get(LoadProgressProperties.PROGRESS),
LoadProgressSimulator.PROGRESS_INCREMENT, MathUtils.EPSILON);
float expectedProgress = LoadProgressSimulator.PROGRESS_INCREMENT * 2;
while (expectedProgress < 1.0f + LoadProgressSimulator.PROGRESS_INCREMENT) {
Shadows.shadowOf(Looper.getMainLooper()).runToEndOfTasks();
assertEquals(mModel.get(LoadProgressProperties.PROGRESS), expectedProgress,
MathUtils.EPSILON);
expectedProgress += LoadProgressSimulator.PROGRESS_INCREMENT;
}
assertEquals(mModel.get(LoadProgressProperties.COMPLETION_STATE),
CompletionState.FINISHED_DO_ANIMATE);
}
}
file://chrome/android/java/src/org/chromium/chrome/browser/toolbar/load_progress/OWNERS
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment