diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java index d89d555c4e26f9..db7231329c196e 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java @@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; import org.chromium.base.Callback; +import org.chromium.base.Log; import org.chromium.base.TraceEvent; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.Supplier; @@ -62,6 +63,8 @@ /** Coordinator for showing UI for a list of tabs. Can be used in GRID or STRIP modes. */ public class TabListCoordinator implements PriceMessageService.PriceWelcomeMessageProvider, DestroyObserver { + private static final String TAG = "TabListCoordinator"; + /** * Modes of showing the list of tabs. * @@ -483,7 +486,15 @@ Size getThumbnailSize() { } void waitForLayoutWithTab(int tabId, Runnable r) { - assert mAwaitingLayoutRunnable == null; + // Very fast navigations to/from the tab list may not have time for a layout to reach a + // completed state. Since this is primarily used for cancellable or skippable animations + // where the runnable will not be serviced downstream, dropping the runnable altogether is + // safe. + if (mAwaitingLayoutRunnable != null) { + Log.d(TAG, "Dropping AwaitingLayoutRunnable for " + mAwaitingTabId); + mAwaitingLayoutRunnable = null; + mAwaitingTabId = Tab.INVALID_TAB_ID; + } int index = getIndexForTabId(tabId); if (index == TabModel.INVALID_TAB_INDEX) { r.run(); diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java index 20019899b67165..19ca400fa09edb 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java @@ -40,6 +40,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.chromium.base.Log; +import org.chromium.chrome.browser.hub.HubFieldTrial; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.tab_ui.R; import org.chromium.ui.base.ViewUtils; @@ -182,9 +183,15 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { * @param runnable the runnable that executes on next layout. */ void runAnimationOnNextLayout(Runnable runnable) { - assert mOnNextLayoutRunnable == null - : "TabListRecyclerView animation on next layout set multiple times without" - + " running."; + // Very fast navigations to/from the tab list may not have time for a layout to reach a + // completed state. Since this is primarily used for cancellable or skippable animations + // where the runnable will not be serviced downstream, dropping the runnable altogether is + // safe for Hub. + if (!HubFieldTrial.isHubEnabled()) { + assert mOnNextLayoutRunnable == null + : "TabListRecyclerView animation on next layout set multiple times without" + + " running."; + } mOnNextLayoutRunnable = () -> { if (mDynamicView == null) {