From 380731a5530f21e761a641f6f1f722dcdfd2c9b5 Mon Sep 17 00:00:00 2001 From: samartnik <30602739+samartnik@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:36:13 -0400 Subject: [PATCH] [Android] Fix `Clear data on exit` not clearing omnibox suggestions (#35790) ShortcutsBackend initializes asynchronously. When clearBrowsingData() was called during startup, the backend was still in the INITIALIZING state and its OnHistoryDeletions handler silently returned early, leaving shortcut suggestions (clock-icon entries) intact after exit. Fix by adding a JNI bridge (BraveShortcutsUtils.initThenRun) that waits for ShortcutsBackend to finish its async DB init via OnShortcutsLoaded() before invoking clearBrowsingData(). Also move the clear-on-exit call from initializeState() to finishNativeInitialization() where the profile is available, and add FORM_DATA (autofill) to the cleared data types. --- android/brave_java_sources.gni | 1 + .../chrome/browser/app/BraveActivity.java | 38 ++++++----- .../browsing_data/BraveShortcutsUtils.java | 34 ++++++++++ browser/android/BUILD.gn | 1 + browser/android/browsing_data/BUILD.gn | 19 ++++++ .../browsing_data/brave_shortcuts_utils.cc | 64 +++++++++++++++++++ build/android/config.gni | 1 + 7 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 android/java/org/chromium/chrome/browser/browsing_data/BraveShortcutsUtils.java create mode 100644 browser/android/browsing_data/BUILD.gn create mode 100644 browser/android/browsing_data/brave_shortcuts_utils.cc diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni index 591b13fb094..7e2b152b1ae 100644 --- a/android/brave_java_sources.gni +++ b/android/brave_java_sources.gni @@ -93,6 +93,7 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/brave_stats/BraveStatsBottomSheetDialogFragment.java", "../../brave/android/java/org/chromium/chrome/browser/brave_stats/BraveStatsUtil.java", "../../brave/android/java/org/chromium/chrome/browser/browsing_data/BraveClearBrowsingDataFragment.java", + "../../brave/android/java/org/chromium/chrome/browser/browsing_data/BraveShortcutsUtils.java", "../../brave/android/java/org/chromium/chrome/browser/compositor/layouts/BraveToolbarSwipeLayout.java", "../../brave/android/java/org/chromium/chrome/browser/contextmenu/BraveChromeContextMenuPopulator.java", "../../brave/android/java/org/chromium/chrome/browser/cosmetic_filters/BraveCosmeticFiltersUtils.java", diff --git a/android/java/org/chromium/chrome/browser/app/BraveActivity.java b/android/java/org/chromium/chrome/browser/app/BraveActivity.java index aa7c4754986..ba53916be19 100644 --- a/android/java/org/chromium/chrome/browser/app/BraveActivity.java +++ b/android/java/org/chromium/chrome/browser/app/BraveActivity.java @@ -124,6 +124,7 @@ import org.chromium.chrome.browser.brave_shields.FirstPartyStorageCleanerAnimati import org.chromium.chrome.browser.brave_shields.FirstPartyStorageCleanerInterface; import org.chromium.chrome.browser.brave_stats.BraveStatsBottomSheetDialogFragment; import org.chromium.chrome.browser.brave_stats.BraveStatsUtil; +import org.chromium.chrome.browser.browsing_data.BraveShortcutsUtils; import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge; import org.chromium.chrome.browser.browsing_data.BrowsingDataType; import org.chromium.chrome.browser.browsing_data.TimePeriod; @@ -1002,21 +1003,6 @@ public abstract class BraveActivity extends ChromeActivity CommandLine.getInstance().appendSwitch(ChromeSwitches.NO_RESTORE_STATE); } - if (isClearBrowsingDataOnExit()) { - List dataTypes = - Arrays.asList( - BrowsingDataType.HISTORY, - BrowsingDataType.SITE_DATA, - BrowsingDataType.CACHE); - - int[] dataTypesArray = CollectionUtil.integerCollectionToIntArray(dataTypes); - - // has onBrowsingDataCleared() as an @Override callback from implementing - // BrowsingDataBridge.OnClearBrowsingDataListener - BrowsingDataBridge.getForProfile(getCurrentProfile()) - .clearBrowsingData(this, dataTypesArray, TimePeriod.ALL_TIME); - } - setLoadedFeed(false); setComesFromNewTab(false); setNewsItemsFeedCards(null); @@ -1514,6 +1500,28 @@ public abstract class BraveActivity extends ChromeActivity } ContextUtils.getAppSharedPreferences().registerOnSharedPreferenceChangeListener(this); + + if (isClearBrowsingDataOnExit()) { + int[] dataTypesArray = + CollectionUtil.integerCollectionToIntArray( + Arrays.asList( + BrowsingDataType.HISTORY, + BrowsingDataType.SITE_DATA, + BrowsingDataType.CACHE)); + PostTask.postTask( + TaskTraits.UI_DEFAULT, + () -> { + // Force-initialize ShortcutsBackend before clearing so that + // OnHistoryDeletions reaches an initialized backend and correctly + // removes omnibox shortcut suggestions. + BraveShortcutsUtils.initThenRun( + getCurrentProfile(), + () -> + BrowsingDataBridge.getForProfile(getCurrentProfile()) + .clearBrowsingData( + this, dataTypesArray, TimePeriod.ALL_TIME)); + }); + } } private void applyChangesForYahooJp() { diff --git a/android/java/org/chromium/chrome/browser/browsing_data/BraveShortcutsUtils.java b/android/java/org/chromium/chrome/browser/browsing_data/BraveShortcutsUtils.java new file mode 100644 index 00000000000..0f0a6ae3169 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/browsing_data/BraveShortcutsUtils.java @@ -0,0 +1,34 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package org.chromium.chrome.browser.browsing_data; + +import org.jni_zero.JNINamespace; +import org.jni_zero.NativeMethods; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.chrome.browser.profiles.Profile; + +/** Utility to ensure ShortcutsBackend is initialized before clearing browsing data. */ +@JNINamespace("brave") +@NullMarked +public class BraveShortcutsUtils { + /** + * Force-initializes ShortcutsBackend for the given profile, then invokes {@code callback}. + * + *

If the backend is already initialized the callback runs synchronously. Otherwise it is + * deferred until the backend's async DB init completes (OnShortcutsLoaded), so that a + * subsequent clearBrowsingData call will see an initialized backend and correctly handle + * OnHistoryDeletions. + */ + public static void initThenRun(Profile profile, Runnable callback) { + BraveShortcutsUtilsJni.get().initThenRun(profile, callback); + } + + @NativeMethods + interface Natives { + void initThenRun(Profile profile, Runnable callback); + } +} diff --git a/browser/android/BUILD.gn b/browser/android/BUILD.gn index 2afa0119033..e368c57442a 100644 --- a/browser/android/BUILD.gn +++ b/browser/android/BUILD.gn @@ -22,6 +22,7 @@ source_set("android_browser_process") { deps = [ "//base", "//brave/brave_domains/android:jni_headers", + "//brave/browser/android/browsing_data", "//brave/browser/android/preferences", "//brave/browser/android/safe_browsing", "//brave/browser/brave_origin/android:jni_headers", diff --git a/browser/android/browsing_data/BUILD.gn b/browser/android/browsing_data/BUILD.gn new file mode 100644 index 00000000000..4521908b7bb --- /dev/null +++ b/browser/android/browsing_data/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright (c) 2026 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +assert(is_android) + +source_set("browsing_data") { + sources = [ "brave_shortcuts_utils.cc" ] + + deps = [ + "//base", + "//chrome/android:chrome_jni_headers", + "//chrome/browser/autocomplete", + "//chrome/browser/profiles:profile", + "//components/omnibox/browser", + "//third_party/jni_zero", + ] +} diff --git a/browser/android/browsing_data/brave_shortcuts_utils.cc b/browser/android/browsing_data/brave_shortcuts_utils.cc new file mode 100644 index 00000000000..65a9993417a --- /dev/null +++ b/browser/android/browsing_data/brave_shortcuts_utils.cc @@ -0,0 +1,64 @@ +/* Copyright (c) 2026 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include + +#include "base/android/scoped_java_ref.h" +#include "chrome/android/chrome_jni_headers/BraveShortcutsUtils_jni.h" +#include "chrome/browser/autocomplete/shortcuts_backend_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "components/omnibox/browser/shortcuts_backend.h" +#include "third_party/jni_zero/common_apis.h" + +namespace brave { + +namespace { + +// One-shot observer: waits for ShortcutsBackend to finish its async DB init, +// then runs the Java callback (which calls clearBrowsingData). At that point +// OnHistoryDeletions will see an initialized backend and handle shortcuts +// correctly without any extra workarounds. +class ShortcutsInitCallbackObserver + : public ShortcutsBackend::ShortcutsBackendObserver { + public: + ShortcutsInitCallbackObserver( + scoped_refptr backend, + base::android::ScopedJavaGlobalRef j_callback) + : backend_(std::move(backend)), j_callback_(std::move(j_callback)) { + backend_->AddObserver(this); + } + ~ShortcutsInitCallbackObserver() override { backend_->RemoveObserver(this); } + + void OnShortcutsLoaded() override { + jni_zero::RunRunnable(j_callback_); + delete this; + } + void OnShortcutsChanged() override {} + + private: + scoped_refptr backend_; + base::android::ScopedJavaGlobalRef j_callback_; +}; + +} // namespace + +void JNI_BraveShortcutsUtils_InitThenRun( + JNIEnv* env, + const jni_zero::JavaRef& j_profile, + const jni_zero::JavaRef& j_callback) { + Profile* profile = Profile::FromJavaObject(j_profile); + auto backend = ShortcutsBackendFactory::GetForProfile(profile); + if (!backend || backend->initialized()) { + jni_zero::RunRunnable(j_callback); + return; + } + new ShortcutsInitCallbackObserver( + std::move(backend), + base::android::ScopedJavaGlobalRef(j_callback)); +} + +} // namespace brave + +DEFINE_JNI(BraveShortcutsUtils) diff --git a/build/android/config.gni b/build/android/config.gni index 6294dbb90be..89bc6e90f33 100644 --- a/build/android/config.gni +++ b/build/android/config.gni @@ -140,6 +140,7 @@ brave_jni_headers_sources = [ "//brave/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoMojomHelper.java", "//brave/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoUtils.java", "//brave/android/java/org/chromium/chrome/browser/brave_news/BraveNewsControllerFactory.java", + "//brave/android/java/org/chromium/chrome/browser/browsing_data/BraveShortcutsUtils.java", "//brave/android/java/org/chromium/chrome/browser/cosmetic_filters/BraveCosmeticFiltersUtils.java", "//brave/android/java/org/chromium/chrome/browser/misc_metrics/MiscAndroidMetricsFactory.java", "//brave/android/java/org/chromium/chrome/browser/ntp_background_images/NTPBackgroundImagesBridge.java",