Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit ff33bbeb authored by Carmelo Messina's avatar Carmelo Messina
Browse files

Enable extensions in android: allow use with always incognito (#2519 #2511)...

Enable extensions in android: allow use with always incognito (#2519 #2511) and fix dangling raw_ptr on destruction
parent 224c227e
Loading
Loading
Loading
Loading
+109 −2
Original line number Diff line number Diff line
@@ -3,8 +3,12 @@ Date: Thu, 6 Nov 2025 13:22:24 +0000
Subject: Backport-v144-android-extensions-changes

Enable support for anonymous browsing.
Backport of bugid 7089957 extensions Use BrowserWindowInterface in ExtensionViewHost
Backport of bugid
7089957: extensions Use BrowserWindowInterface in ExtensionViewHost
https://chromium-review.googlesource.com/c/chromium/src/+/7089957

7126008: Destroy ExtensionWindowControllerBridge when its Profile is destroyed
https://chromium-review.googlesource.com/c/chromium/src/+/7126008
---
 .../chrome/browser/ChromeTabbedActivity.java  |   1 +
 .../chrome/browser/app/ChromeActivity.java    |  12 ++
@@ -20,13 +24,15 @@ https://chromium-review.googlesource.com/c/chromium/src/+/7089957
 chrome/browser/ui/android/extensions/BUILD.gn |   1 +
 .../extension_action_popup_contents.cc        |   9 +-
 .../ExtensionActionPopupContents.java         |  14 +-
 .../extensions/windowing/internal/BUILD.gn    |   1 +
 .../ExtensionWindowControllerBridgeImpl.java  |  45 ++++++-
 chrome/browser/ui/android/toolbar/BUILD.gn    |   2 +
 .../ExtensionActionListCoordinator.java       |  10 +-
 .../ExtensionActionListMediator.java          |  16 ++-
 .../ExtensionActionListMediatorTest.java      |  11 +-
 .../ExtensionToolbarCoordinator.java          |   5 +
 .../ExtensionToolbarCoordinatorImpl.java      |   4 +
 20 files changed, 160 insertions(+), 92 deletions(-)
 22 files changed, 201 insertions(+), 97 deletions(-)

diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -597,6 +603,107 @@ diff --git a/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/b
 
         /**
          * Destroys the native ExtensionActionPopupContents object.
diff --git a/chrome/browser/ui/android/extensions/windowing/internal/BUILD.gn b/chrome/browser/ui/android/extensions/windowing/internal/BUILD.gn
--- a/chrome/browser/ui/android/extensions/windowing/internal/BUILD.gn
+++ b/chrome/browser/ui/android/extensions/windowing/internal/BUILD.gn
@@ -38,6 +38,7 @@ android_library("java") {
   ]
   deps = [
     "//build/android:build_java",
+    "//chrome/browser/profiles/android:java",
     "//chrome/browser/ui/android/extensions/windowing:java",
     "//chrome/browser/ui/browser_window/public/android:java",
     "//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chrome/browser/ui/android/extensions/windowing/internal/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeImpl.java b/chrome/browser/ui/android/extensions/windowing/internal/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeImpl.java
--- a/chrome/browser/ui/android/extensions/windowing/internal/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeImpl.java
+++ b/chrome/browser/ui/android/extensions/windowing/internal/java/src/org/chromium/chrome/browser/ui/extensions/windowing/ExtensionWindowControllerBridgeImpl.java
@@ -11,6 +11,8 @@ import org.jni_zero.CalledByNative;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.ui.browser_window.ChromeAndroidTask;
 
 import java.util.ArrayList;
@@ -19,7 +21,8 @@ import java.util.Map;
 
 /** Implements {@link ExtensionWindowControllerBridge}. */
 @NullMarked
-final class ExtensionWindowControllerBridgeImpl implements ExtensionWindowControllerBridge {
+final class ExtensionWindowControllerBridgeImpl
+        implements ExtensionWindowControllerBridge, ProfileManager.Observer {
 
     /**
      * Events received by the native singleton {@code WindowControllerListObserverForTesting}.
@@ -82,6 +85,7 @@ final class ExtensionWindowControllerBridgeImpl implements ExtensionWindowContro
         assert mNativeExtensionWindowControllerBridge == 0
                 : "ExtensionWindowControllerBridge is already added to a task.";
 
+        ProfileManager.addObserver(this);
         mNativeExtensionWindowControllerBridge =
                 ExtensionWindowControllerBridgeImplJni.get()
                         .create(
@@ -91,10 +95,8 @@ final class ExtensionWindowControllerBridgeImpl implements ExtensionWindowContro
 
     @Override
     public void onTaskRemoved() {
-        if (mNativeExtensionWindowControllerBridge != 0) {
-            ExtensionWindowControllerBridgeImplJni.get()
-                    .destroy(mNativeExtensionWindowControllerBridge);
-        }
+        destroyNativeExtensionWindowControllerBridge();
+        ProfileManager.removeObserver(this);
     }
 
     @Override
@@ -113,6 +115,32 @@ final class ExtensionWindowControllerBridgeImpl implements ExtensionWindowContro
         }
     }
 
+    @Override
+    public void onProfileAdded(Profile profile) {}
+
+    @Override
+    public void onProfileDestroyed(Profile profile) {
+        // This is a short-term fix for http://crbug.com/450234852.
+        //
+        // The native extension code is cross-platform and depends on BrowserWindowInterface. Per
+        // documentation of BrowserWindowInterface::GetProfile(), cross-platform code assumes
+        // BrowserWindowInterface will always be destroyed *before* its Profile.
+        //
+        // See:
+        // https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/browser_window/public/browser_window_interface.h;l=108;drc=c8dc70b538f1bb0862f1be58237d6e945ee81819
+        //
+        // However, on Android, http://crbug.com/450234852 reveals a case where the Profile is
+        // destroyed before its BrowserWindowInterface, which caused native extension code to
+        // dereference an invalid Profile pointer.
+        //
+        // To avoid this issue, we destroy the native objects for extensions as soon as the native
+        // Profile is about to be destroyed. The long-term fix should be to correct the object
+        // destruction order for the case described in http://crbug.com/450234852.
+        if (profile == mChromeAndroidTask.getProfile()) {
+            destroyNativeExtensionWindowControllerBridge();
+        }
+    }
+
     long getNativePtrForTesting() {
         return mNativeExtensionWindowControllerBridge;
     }
@@ -123,6 +151,13 @@ final class ExtensionWindowControllerBridgeImpl implements ExtensionWindowContro
                 .getExtensionWindowIdForTesting(mNativeExtensionWindowControllerBridge); // IN-TEST
     }
 
+    private void destroyNativeExtensionWindowControllerBridge() {
+        if (mNativeExtensionWindowControllerBridge != 0) {
+            ExtensionWindowControllerBridgeImplJni.get()
+                    .destroy(mNativeExtensionWindowControllerBridge);
+        }
+    }
+
     @CalledByNative
     private void clearNativePtr() {
         mNativeExtensionWindowControllerBridge = 0;
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
+68 −8
Original line number Diff line number Diff line
@@ -12,22 +12,25 @@ License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
 .../BaseCustomTabRootUiCoordinator.java       |  4 +-
 .../CustomTabIntentDataProvider.java          |  3 +
 .../tabbed_mode/TabbedRootUiCoordinator.java  |  3 +-
 .../browser/tabmodel/TabModelJniBridge.java   |  4 +-
 .../tabmodel/TabModelSelectorBase.java        |  7 ++
 .../browser/toolbar/ToolbarManager.java       |  4 +-
 .../chrome/browser/ui/RootUiCoordinator.java  |  6 +-
 .../developer_private_functions_shared.cc     |  4 +-
 .../browser/extensions/extension_tab_util.cc  |  2 +-
 .../tabmodel/IncognitoTabModelImpl.java       |  5 +
 .../tabmodel/IncognitoTabModelImpl.java       | 10 +-
 .../tabmodel/IncognitoTabModelObserver.java   |  2 +
 .../extensions/extension_actions_bridge.cc    | 12 +++
 .../extensions/extension_actions_bridge.h     |  1 +
 .../ui/extensions/ExtensionActionsBridge.java |  6 ++
 .../android/tab_model/tab_model_jni_bridge.cc |  4 +
 .../ExtensionActionListCoordinator.java       |  4 +-
 .../ExtensionActionListMediator.java          | 16 ++--
 .../ExtensionToolbarCoordinator.java          |  7 +-
 .../ExtensionToolbarCoordinatorImpl.java      |  5 +-
 .../ChromeAndroidTaskTrackerImpl.java         | 93 ++++++++++++++-----
 .../ChromeAndroidTaskTracker.java             |  6 +-
 21 files changed, 201 insertions(+), 51 deletions(-)
 24 files changed, 213 insertions(+), 56 deletions(-)

diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -245,6 +248,22 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/Tab
             @NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher,
             @NonNull ObservableSupplier<LayoutManagerImpl> layoutManagerSupplier,
             @NonNull MenuOrKeyboardActionController menuOrKeyboardActionController,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelJniBridge.java
@@ -129,10 +129,10 @@ public abstract class TabModelJniBridge implements TabModelInternal {
     @Override
     public void associateWithBrowserWindow(long nativeAndroidBrowserWindow) {
         // Ensure this isn't set multiple times.
-        assert mNativeAndroidBrowserWindow == 0;
+        if (nativeAndroidBrowserWindow != 0) assert mNativeAndroidBrowserWindow == 0;
         mNativeAndroidBrowserWindow = nativeAndroidBrowserWindow;
 
-        assert nativeAndroidBrowserWindow != 0;
+        // assert nativeAndroidBrowserWindow != 0;
         TabModelJniBridgeJni.get()
                 .associateWithBrowserWindow(mNativeTabModelJniBridge, nativeAndroidBrowserWindow);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java
@@ -327,6 +346,20 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordi
             @NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher,
             @NonNull ObservableSupplier<LayoutManagerImpl> layoutManagerSupplier,
             @NonNull MenuOrKeyboardActionController menuOrKeyboardActionController,
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc b/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc
--- a/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc
@@ -1676,8 +1676,8 @@ DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction::Run() {
   }
 
   Profile* profile = Profile::FromBrowserContext(browser_context());
-  SafetyHubMenuNotificationServiceFactory::GetForProfile(profile)
-      ->DismissActiveNotificationOfModule(
+  if (auto* menu_notification_service_factory = SafetyHubMenuNotificationServiceFactory::GetForProfile(profile))
+      menu_notification_service_factory->DismissActiveNotificationOfModule(
           safety_hub::SafetyHubModuleType::EXTENSIONS);
   return RespondNow(NoArguments());
 }
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -342,18 +375,31 @@ diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/ex
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelImpl.java
@@ -151,6 +151,11 @@ class IncognitoTabModelImpl implements IncognitoTabModelInternal {
         mDelegateModel.destroy();
         mCurrentTabSupplier.set(null);
         mTabCountSupplier.set(0);
@@ -140,8 +140,12 @@ class IncognitoTabModelImpl implements IncognitoTabModelInternal {
             return;
         }
 
+        // Disassociate
+        mDelegateModel.associateWithBrowserWindow(/*nativeAndroidBrowserWindow=*/ 0);
+        mNativeAndroidBrowserWindow = 0;
+
+        for (IncognitoTabModelObserver observer : mIncognitoObservers) {
         for (IncognitoTabModelObserver observer : mIncognitoObservers) {
-            observer.didBecomeEmpty();
+            observer.didDestroyed();
+        }
         }
 
         mDelegateModel
@@ -152,6 +156,10 @@ class IncognitoTabModelImpl implements IncognitoTabModelInternal {
         mCurrentTabSupplier.set(null);
         mTabCountSupplier.set(0);
 
+        for (IncognitoTabModelObserver observer : mIncognitoObservers) {
+            observer.didBecomeEmpty();
+        }
+
         mDelegateModel = EmptyTabModel.getInstance(true);
         for (Callback<TabModelInternal> delegateModelObserver : mDelegateModelObservers) {
             delegateModelObserver.onResult(mDelegateModel);
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelObserver.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelObserver.java
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelObserver.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModelObserver.java
@@ -427,6 +473,20 @@ diff --git a/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/b
         HandleKeyEventResult handleKeyDownEvent(
                 long nativeExtensionActionsBridge,
                 @JniType("ui::KeyEventAndroid") KeyEvent keyEvent);
diff --git a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
--- a/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_jni_bridge.cc
@@ -116,6 +116,10 @@ void TabModelJniBridge::AssociateWithBrowserWindow(
 // BrowserWindowInterface is available on desktop Android, but not other Android
 // builds. For non-desktop Android, this function should be a no-op.
 #if BUILDFLAG(IS_DESKTOP_ANDROID_CROMITE)
+  if (native_android_browser_window == 0) {
+    scoped_unowned_user_data_.reset();
+    return;
+  }
   BrowserWindowInterface* android_browser_window =
       reinterpret_cast<BrowserWindowInterface*>(native_android_browser_window);
   CHECK(android_browser_window != nullptr);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionListCoordinator.java
+75 −4
Original line number Diff line number Diff line
@@ -81,12 +81,13 @@ License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html
 chrome/browser/startup_data.h                 |  6 +-
 .../common/task_manager_features.cc           |  2 +-
 chrome/browser/ui/BUILD.gn                    |  6 +-
 .../browser/ui/android/context_menu_helper.cc |  6 +-
 .../browser/ui/android/context_menu_helper.cc | 12 +++-
 .../browser/ui/android/context_menu_helper.h  |  4 +-
 chrome/browser/ui/android/extensions/BUILD.gn |  4 +-
 .../extensions/extension_actions_bridge.cc    | 23 ++++++
 .../extensions/extension_actions_bridge.h     |  1 +
 .../res/layout/extensions_menu_footer.xml     |  3 +-
 .../java/res/layout/extensions_menu.xml       |  1 +
 .../res/layout/extensions_menu_footer.xml     |  4 +-
 .../extensions/java/res/values/dimens.xml     |  1 +
 .../ui/extensions/ExtensionActionsBridge.java |  6 ++
 .../extensions/windowing/internal/BUILD.gn    |  2 +-
@@ -123,6 +124,8 @@ License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html
 chrome/common/extensions/api/api_sources.gni  |  7 ++
 chrome/common/webui_url_constants.cc          |  2 +-
 chrome/common/webui_url_constants.h           |  2 +-
 .../sandbox_status_extension_android.cc       |  3 +-
 .../sandbox_status_extension_android.h        |  1 +
 chrome/test/BUILD.gn                          | 14 ++--
 chrome/version.gni                            |  4 +-
 .../core/browser/host_content_settings_map.cc |  6 ++
@@ -183,7 +186,7 @@ License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html
 ui/webui/resources/cr_elements/BUILD.gn       |  2 +-
 .../cr_elements/cr_toolbar/cr_toolbar.css     |  6 ++
 ui/webui/resources/css/BUILD.gn               |  2 +-
 171 files changed, 561 insertions(+), 288 deletions(-)
 174 files changed, 572 insertions(+), 289 deletions(-)
 create mode 100644 chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Enable-extensions-Android.grdp
 create mode 100644 chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Extensions-Android.grdp
 create mode 100644 cromite_flags/chrome/browser/about_flags_cc/Extensions-Android.inc
@@ -1671,6 +1674,24 @@ diff --git a/chrome/browser/ui/android/context_menu_helper.cc b/chrome/browser/u
   // Reset any previous menu model, in case a new menu is shown
   // before the old one was gracefully closed.
   extension_menu_model_.reset();
@@ -82,11 +82,17 @@ void ContextMenuHelper::ShowContextMenu(
 }
 
 void ContextMenuHelper::DismissContextMenu() {
+#if BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS_CROMITE)
+  extension_menu_model_.reset();
+#endif  // BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_ContextMenuHelper_dismissContextMenu(env, java_obj_);
 }
 
 void ContextMenuHelper::OnContextMenuClosed(JNIEnv* env) {
+#if BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS_CROMITE)
+  extension_menu_model_.reset();
+#endif  // BUILDFLAG(ENABLE_DESKTOP_ANDROID_EXTENSIONS)
   GetWebContents().NotifyContextMenuClosed(context_menu_params_.link_followed,
                                            context_menu_params_.impression);
 }
diff --git a/chrome/browser/ui/android/context_menu_helper.h b/chrome/browser/ui/android/context_menu_helper.h
--- a/chrome/browser/ui/android/context_menu_helper.h
+++ b/chrome/browser/ui/android/context_menu_helper.h
@@ -1762,6 +1783,17 @@ diff --git a/chrome/browser/ui/android/extensions/extension_actions_bridge.h b/c
   jni_zero::ScopedJavaLocalRef<jobject> HandleKeyDownEvent(
       JNIEnv* env,
       const ui::KeyEventAndroid& key_event);
diff --git a/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu.xml b/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu.xml
--- a/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu.xml
+++ b/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu.xml
@@ -49,6 +49,7 @@ found in the LICENSE file.
 
     <View
         android:id="@+id/extensions_menu_footer_divider"
+        android:visibility="gone"
         android:layout_width="0dp"
         android:layout_height="1dp"
         app:layout_constraintTop_toBottomOf="@+id/extensions_menu_items"
diff --git a/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu_footer.xml b/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu_footer.xml
--- a/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu_footer.xml
+++ b/chrome/browser/ui/android/extensions/java/res/layout/extensions_menu_footer.xml
@@ -1773,7 +1805,15 @@ diff --git a/chrome/browser/ui/android/extensions/java/res/layout/extensions_men
         style="?android:attr/borderlessButtonStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -43,4 +44,4 @@ found in the LICENSE file.
@@ -29,6 +30,7 @@ found in the LICENSE file.
 
     <androidx.appcompat.widget.AppCompatButton
         android:id="@+id/extensions_menu_manage_extensions_button"
+        android:visibility="gone"
         style="?android:attr/borderlessButtonStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -43,4 +45,4 @@ found in the LICENSE file.
         android:drawableTint="?android:attr/textColorPrimary"
         android:gravity="start|center_vertical"/>
 
@@ -2512,6 +2552,37 @@ diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_const
 inline constexpr char kChromeUIDiscardsHost[] = "discards";
 inline constexpr char kChromeUIDiscardsURL[] = "chrome://discards/";
 #endif
diff --git a/chrome/renderer/sandbox_status_extension_android.cc b/chrome/renderer/sandbox_status_extension_android.cc
--- a/chrome/renderer/sandbox_status_extension_android.cc
+++ b/chrome/renderer/sandbox_status_extension_android.cc
@@ -34,7 +34,7 @@ SandboxStatusExtension::SandboxStatusExtension(content::RenderFrame* frame)
   frame->GetAssociatedInterfaceRegistry()
       ->AddInterface<chrome::mojom::SandboxStatusExtension>(base::BindRepeating(
           &SandboxStatusExtension::OnSandboxStatusExtensionRequest,
-          base::RetainedRef(this)));
+          weak_ptr_factory_.GetWeakPtr()));
 }
 
 SandboxStatusExtension::~SandboxStatusExtension() = default;
@@ -46,6 +46,7 @@ void SandboxStatusExtension::Create(content::RenderFrame* frame) {
 }
 
 void SandboxStatusExtension::OnDestruct() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   // This object is ref-counted, since a callback could still be in-flight.
   Release();
 }
diff --git a/chrome/renderer/sandbox_status_extension_android.h b/chrome/renderer/sandbox_status_extension_android.h
--- a/chrome/renderer/sandbox_status_extension_android.h
+++ b/chrome/renderer/sandbox_status_extension_android.h
@@ -73,6 +73,7 @@ class SandboxStatusExtension
 
   mojo::AssociatedReceiver<chrome::mojom::SandboxStatusExtension> receiver_{
       this};
+  base::WeakPtrFactory<SandboxStatusExtension> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_RENDERER_SANDBOX_STATUS_EXTENSION_ANDROID_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn