From 841cad239e0dd95e839913a2383e16e15c96ea7e Mon Sep 17 00:00:00 2001 From: Serg Date: Mon, 30 Mar 2026 17:55:32 -0400 Subject: [PATCH] [Android] Defer bottom toolbar ViewStub inflation to fix startup ANR (#35083) Move the bottom controls setup (ViewStub inflation, coordinator creation, native initialization) from synchronous execution during finishNativeInitialization to a deferred UI task via PostTask. The synchronous inflation was triggering HardwareRenderer.nNotifyExpensiveFrame which blocked the main thread waiting for the GPU, causing ANR on devices under GPU pressure during startup. This is safe because all consumers of mBottomControlsCoordinatorSupplier and mTabGroupUiOneshotSupplier already null-check before use, and the bottom toolbar starts with visibility="gone" making the one-frame delay imperceptible. The task is wrapped with mCallbackController.makeCancelable() to auto-cancel during Activity teardown. Resolves: https://github.com/brave/brave-browser/issues/54077 --- .../browser/toolbar/BraveToolbarManager.java | 242 +++++++++--------- 1 file changed, 128 insertions(+), 114 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/toolbar/BraveToolbarManager.java b/android/java/org/chromium/chrome/browser/toolbar/BraveToolbarManager.java index 3b74feb262a..8869eb91a5b 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/BraveToolbarManager.java +++ b/android/java/org/chromium/chrome/browser/toolbar/BraveToolbarManager.java @@ -24,6 +24,8 @@ import org.chromium.base.supplier.NullableObservableSupplier; import org.chromium.base.supplier.OneshotSupplier; import org.chromium.base.supplier.SettableMonotonicObservableSupplier; import org.chromium.base.supplier.SettableNonNullObservableSupplier; +import org.chromium.base.task.PostTask; +import org.chromium.base.task.TaskTraits; import org.chromium.build.annotations.Nullable; import org.chromium.cc.input.BrowserControlsState; import org.chromium.chrome.R; @@ -144,7 +146,11 @@ public class BraveToolbarManager extends ToolbarManager private TopUiThemeColorProvider mTopUiThemeColorProvider; private int mCurrentOrientation; private boolean mInitializedWithNative; + + // Deleted in bytecode; parent class field is used instead. + @SuppressWarnings("FieldCanBeLocal") private @Nullable TabGroupUiOneshotSupplier mTabGroupUiOneshotSupplier; + private @Nullable UndoBarThrottle mUndoBarThrottle; // Own members. @@ -317,123 +323,131 @@ public class BraveToolbarManager extends ToolbarManager super.enableBottomControls(); return; } - ViewStub bottomControlsStub = - (ViewStub) mActivity.findViewById(R.id.bottom_controls_stub); - mBottomControls = - (BraveScrollingBottomViewResourceFrameLayout) bottomControlsStub.inflate(); + } + // Defer bottom controls inflation to avoid ANR from GPU contention + // during startup. All consumers null-check the coordinator supplier. + // The bottom toolbar starts with visibility="gone", so the one-frame + // delay is imperceptible to the user. Using makeCancelable ensures + // the task is auto-cancelled when mCallbackController.destroy() is + // called during Activity teardown, preventing use-after-destroy. + PostTask.postTask( + TaskTraits.UI_DEFAULT, + mCallbackController.makeCancelable(this::initBraveBottomControls)); + } - ThemeColorProvider bottomUiThemeColorProvider = - new BottomUiThemeColorProvider( - mTopUiThemeColorProvider, - mBrowserControlsSizer, - mBottomControlsStacker, - mIncognitoStateProvider, - mActivity); + private void initBraveBottomControls() { + ViewStub bottomControlsStub = (ViewStub) mActivity.findViewById(R.id.bottom_controls_stub); + mBottomControls = + (BraveScrollingBottomViewResourceFrameLayout) bottomControlsStub.inflate(); - mTabGroupUiOneshotSupplier = - new TabGroupUiOneshotSupplier( - mActivityTabProvider, - mTabModelSelector, - mActivity, - mBottomControls.findViewById(R.id.bottom_container_slot), - mBrowserControlsSizer, - mScrimManager, - mOmniboxFocusStateSupplier, - mBottomSheetController, - mDataSharingTabManager, - mTabContentManager, - mTabCreatorManager, - mLayoutStateProviderSupplier, - mModalDialogManagerSupplier.get(), - bottomUiThemeColorProvider, - mUndoBarThrottle, - mTabBookmarkerSupplier, - mShareDelegateSupplier); - var bottomControlsContentDelegateSupplier = - (OneshotSupplier) - ((OneshotSupplier) - mTabGroupUiOneshotSupplier); - - BrowserStateBrowserControlsVisibilityDelegate controlsVisibilityDelegate = - mBrowserControlsSizer.getBrowserVisibilityDelegate(); - assert controlsVisibilityDelegate != null; - var bottomControlsCoordinator = - new BraveBottomControlsCoordinator( - mLayoutStateProviderSupplier, - BottomTabSwitcherActionMenuCoordinator.createOnLongClickListener( - id -> - ((ChromeActivity) mActivity) - .onOptionsItemSelected(id, null), - mProfileSupplier.get(), - mTabModelSelectorSupplier), - mActivityTabProvider, - mToolbarTabController::openHomepage, - mCallbackController.makeCancelable( - (reason) -> setUrlBarFocus(true, reason)), - mMenuButtonCoordinator.getMenuButtonHelperSupplier(), - mAppThemeColorProvider, - mBookmarkModelSupplier, - mLocationBarModel, - /* Below are parameters for BottomControlsCoordinator */ - mWindowAndroid, - mLayoutManager, - mCompositorViewHolder.getResourceManager(), - mBottomControlsStacker, - controlsVisibilityDelegate, - mFullscreenManager, - mEdgeToEdgeControllerSupplier, - mBottomControls, - bottomControlsContentDelegateSupplier, - mTabObscuringHandler, - mOverlayPanelVisibilitySupplier, - getConstraintsProxy(), - /* readAloudRestoringSupplier= */ () -> { - final var readAloud = mReadAloudControllerSupplier.get(); - return readAloud != null && readAloud.isRestoringPlayer(); - }); - if (mInitializedWithNative) { - Runnable closeAllTabsAction = - () -> { - mTabModelSelector - .getModel(mIncognitoStateProvider.isIncognitoSelected()) - .getTabRemover() - .closeTabs(TabClosureParams.closeAllTabs().build(), false); - }; - - assert (mActivity instanceof ChromeActivity); - OnClickListener wrappedNewTabClickHandler = - v -> { - recordNewTabClick(); - ((ChromeActivity) mActivity) - .getMenuOrKeyboardActionController() - .onMenuOrKeyboardAction( - mIncognitoStateProvider.isIncognitoSelected() - ? R.id.new_incognito_tab_menu_id - : R.id.new_tab_menu_id, - false); - }; - - bottomControlsCoordinator.initializeWithNative( - mActivity, - mCompositorViewHolder.getResourceManager(), - mCompositorViewHolder.getLayoutManager(), - /*tabSwitcherListener*/ v -> mOpenGridTabSwitcherHandler.run(), - /*newTabClickListener*/ wrappedNewTabClickHandler, - mWindowAndroid, - mTabModelSelector, + ThemeColorProvider bottomUiThemeColorProvider = + new BottomUiThemeColorProvider( + mTopUiThemeColorProvider, + mBrowserControlsSizer, + mBottomControlsStacker, mIncognitoStateProvider, - mActivity.findViewById(R.id.control_container), - closeAllTabsAction); - } - assert mBottomControlsCoordinatorSupplier != null - : "It must not be null at this point! Something has changed in the upstream!"; - mBottomControlsCoordinatorSupplier.set(bottomControlsCoordinator); - mBottomControls.setBottomControlsCoordinatorSupplier( - mBottomControlsCoordinatorSupplier); - updateBraveBottomControlsVisibility(); - if (mIsBraveBottomControlsVisible) { - mBottomControls.setVisibility(View.VISIBLE); - } + mActivity); + + mTabGroupUiOneshotSupplier = + new TabGroupUiOneshotSupplier( + mActivityTabProvider, + mTabModelSelector, + mActivity, + mBottomControls.findViewById(R.id.bottom_container_slot), + mBrowserControlsSizer, + mScrimManager, + mOmniboxFocusStateSupplier, + mBottomSheetController, + mDataSharingTabManager, + mTabContentManager, + mTabCreatorManager, + mLayoutStateProviderSupplier, + mModalDialogManagerSupplier.get(), + bottomUiThemeColorProvider, + mUndoBarThrottle, + mTabBookmarkerSupplier, + mShareDelegateSupplier); + var bottomControlsContentDelegateSupplier = + (OneshotSupplier) + ((OneshotSupplier) + mTabGroupUiOneshotSupplier); + + BrowserStateBrowserControlsVisibilityDelegate controlsVisibilityDelegate = + mBrowserControlsSizer.getBrowserVisibilityDelegate(); + assert controlsVisibilityDelegate != null; + var bottomControlsCoordinator = + new BraveBottomControlsCoordinator( + mLayoutStateProviderSupplier, + BottomTabSwitcherActionMenuCoordinator.createOnLongClickListener( + id -> ((ChromeActivity) mActivity).onOptionsItemSelected(id, null), + mProfileSupplier.get(), + mTabModelSelectorSupplier), + mActivityTabProvider, + mToolbarTabController::openHomepage, + mCallbackController.makeCancelable( + (reason) -> setUrlBarFocus(true, reason)), + mMenuButtonCoordinator.getMenuButtonHelperSupplier(), + mAppThemeColorProvider, + mBookmarkModelSupplier, + mLocationBarModel, + /* Below are parameters for BottomControlsCoordinator */ + mWindowAndroid, + mLayoutManager, + mCompositorViewHolder.getResourceManager(), + mBottomControlsStacker, + controlsVisibilityDelegate, + mFullscreenManager, + mEdgeToEdgeControllerSupplier, + mBottomControls, + bottomControlsContentDelegateSupplier, + mTabObscuringHandler, + mOverlayPanelVisibilitySupplier, + getConstraintsProxy(), + /* readAloudRestoringSupplier= */ () -> { + final var readAloud = mReadAloudControllerSupplier.get(); + return readAloud != null && readAloud.isRestoringPlayer(); + }); + if (mInitializedWithNative) { + Runnable closeAllTabsAction = + () -> { + mTabModelSelector + .getModel(mIncognitoStateProvider.isIncognitoSelected()) + .getTabRemover() + .closeTabs(TabClosureParams.closeAllTabs().build(), false); + }; + + assert (mActivity instanceof ChromeActivity); + OnClickListener wrappedNewTabClickHandler = + v -> { + recordNewTabClick(); + ((ChromeActivity) mActivity) + .getMenuOrKeyboardActionController() + .onMenuOrKeyboardAction( + mIncognitoStateProvider.isIncognitoSelected() + ? R.id.new_incognito_tab_menu_id + : R.id.new_tab_menu_id, + false); + }; + + bottomControlsCoordinator.initializeWithNative( + mActivity, + mCompositorViewHolder.getResourceManager(), + mCompositorViewHolder.getLayoutManager(), + /*tabSwitcherListener*/ v -> mOpenGridTabSwitcherHandler.run(), + /*newTabClickListener*/ wrappedNewTabClickHandler, + mWindowAndroid, + mTabModelSelector, + mIncognitoStateProvider, + mActivity.findViewById(R.id.control_container), + closeAllTabsAction); + } + assert mBottomControlsCoordinatorSupplier != null + : "It must not be null at this point! Something has changed in the upstream!"; + mBottomControlsCoordinatorSupplier.set(bottomControlsCoordinator); + mBottomControls.setBottomControlsCoordinatorSupplier(mBottomControlsCoordinatorSupplier); + updateBraveBottomControlsVisibility(); + if (mIsBraveBottomControlsVisible) { + mBottomControls.setVisibility(View.VISIBLE); } }