tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 802d3f18298937d64882e23b721fb7b5e1c45ab2
parent d314389fc89c949297261180264c1c7ec56aaf08
Author: Cathy Lu <calu@mozilla.com>
Date:   Thu,  8 Jan 2026 20:07:28 +0000

Bug 2008814 - Tab search will only show one about:home result r=android-reviewers,007

Differential Revision: https://phabricator.services.mozilla.com/D278205

Diffstat:
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/redux/middleware/TabSearchMiddleware.kt | 15++++++++++++++-
Mmobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/redux/middleware/TabSearchMiddlewareTest.kt | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/redux/middleware/TabSearchMiddleware.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/redux/middleware/TabSearchMiddleware.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.concept.engine.utils.ABOUT_HOME_URL import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.Store import org.mozilla.fenix.tabstray.Page @@ -47,7 +48,15 @@ class TabSearchMiddleware( val filteredTabs = if (query.isBlank()) { emptyList() } else { - tabs.filter { it.contains(text = query) } + val (matchingHomepage, matchingNonHomepage) = + tabs.filter { it.contains(text = query) } + .sortedByDescending { it.lastAccess } + .partition { it.isHomepage() } + + // If the results contain homepages, only display one homepage result + val homeTab = matchingHomepage.take(1) + + homeTab + matchingNonHomepage } mainScope.launch { @@ -63,4 +72,8 @@ class TabSearchMiddleware( private fun TabSessionState.contains(text: String): Boolean { return content.url.contains(text, ignoreCase = true) || content.title.contains(text, ignoreCase = true) } + + private fun TabSessionState.isHomepage(): Boolean { + return content.url.equals(ABOUT_HOME_URL, ignoreCase = true) + } } diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/redux/middleware/TabSearchMiddlewareTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/tabstray/redux/middleware/TabSearchMiddlewareTest.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.state.createTab +import mozilla.components.concept.engine.utils.ABOUT_HOME_URL import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test @@ -371,4 +372,96 @@ class TabSearchMiddlewareTest { assertTrue(store.state.tabSearchState.searchResults.isEmpty()) } + + @Test + fun `WHEN SearchQueryChanged AND multiple about home tabs match THEN only the most recently accessed about home tab is included`() = runTest { + val aboutHomeOld = createTab(url = ABOUT_HOME_URL, title = "Homepage", lastAccess = 10L) + val aboutHomeNew = createTab(url = ABOUT_HOME_URL, title = "Homepage", lastAccess = 20L) + val aboutHomeNewest = createTab(url = ABOUT_HOME_URL, title = "Homepage", lastAccess = 30L) + + val matchingNonHomepage = listOf( + createTab(url = "mozilla.org/home"), + createTab(url = "mozilla.org", title = "Homepage"), + ) + val nonMatching = listOf( + createTab(url = "example.com"), + ) + + val store = TabsTrayStore( + middlewares = listOf( + TabSearchMiddleware( + scope = this, + mainScope = this, + ), + ), + initialState = TabsTrayState( + selectedPage = Page.NormalTabs, + normalTabs = listOf(aboutHomeOld, aboutHomeNew, aboutHomeNewest) + matchingNonHomepage + nonMatching, + inactiveTabs = emptyList(), + ), + ) + + store.dispatch(TabSearchAction.SearchQueryChanged("home")) + advanceUntilIdle() + + val expectedSearchResults = listOf(aboutHomeNewest) + matchingNonHomepage + assertEquals(expectedSearchResults, store.state.tabSearchState.searchResults) + } + + @Test + fun `WHEN SearchQueryChanged AND about home tabs do not match query THEN no about home tabs are included`() = runTest { + val aboutHomeOld = createTab(url = ABOUT_HOME_URL, title = "Home", lastAccess = 10L) + val aboutHomeNew = createTab(url = ABOUT_HOME_URL, title = "Home", lastAccess = 20L) + + val matchingNonHomepage = listOf( + createTab(url = "mozilla.org"), + ) + + val store = TabsTrayStore( + middlewares = listOf( + TabSearchMiddleware( + scope = this, + mainScope = this, + ), + ), + initialState = TabsTrayState( + selectedPage = Page.NormalTabs, + normalTabs = listOf(aboutHomeOld, aboutHomeNew) + matchingNonHomepage, + inactiveTabs = emptyList(), + ), + ) + + store.dispatch(TabSearchAction.SearchQueryChanged("mozilla")) + advanceUntilIdle() + + assertEquals(matchingNonHomepage, store.state.tabSearchState.searchResults) + } + + @Test + fun `WHEN SearchQueryChanged AND single about home tab matches THEN it is included`() = runTest { + val aboutHome = createTab(url = ABOUT_HOME_URL, title = "Homepage", lastAccess = 42L) + val matchingNonHomepage = listOf( + createTab(url = "mozilla.org/home"), + ) + + val store = TabsTrayStore( + middlewares = listOf( + TabSearchMiddleware( + scope = this, + mainScope = this, + ), + ), + initialState = TabsTrayState( + selectedPage = Page.NormalTabs, + normalTabs = listOf(aboutHome) + matchingNonHomepage, + inactiveTabs = emptyList(), + ), + ) + + store.dispatch(TabSearchAction.SearchQueryChanged("home")) + advanceUntilIdle() + + val expectedSearchResults = listOf(aboutHome) + matchingNonHomepage + assertEquals(expectedSearchResults, store.state.tabSearchState.searchResults) + } }