diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 6e16554665643..1d865135dcd09 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1460,6 +1460,7 @@ android_library("chrome_test_java") {
     "//ui/base/mojom:mojom_java",
     "//url:gurl_java",
     "//url:gurl_javatests",
+    "//url:gurl_junit_test_support",
     "//url:origin_java",
     "//url/mojom:url_mojom_gurl_java",
   ]
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
index 1e4a48ca423f3..c60478e11ba44 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -1092,7 +1092,8 @@ public class InstantStartTest {
     }
 
     private void startAndWaitNativeInitialization() {
-        Assert.assertFalse(LibraryLoader.getInstance().isInitialized());
+        Assert.assertFalse(
+                NativeLibraryLoadedStatus.getProviderForTesting().areMainDexNativeMethodsReady());
 
         CommandLine.getInstance().removeSwitch(ChromeSwitches.DISABLE_NATIVE_INITIALIZATION);
         TestThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTestUtils.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTestUtils.java
index afc8ee91de8c2..b9e390cecc41c 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTestUtils.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTestUtils.java
@@ -62,6 +62,7 @@ import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVis
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -360,14 +361,18 @@ public class StartSurfaceTestUtils {
      */
     public static List<SiteSuggestion> createFakeSiteSuggestions() {
         List<SiteSuggestion> siteSuggestions = new ArrayList<>();
-        siteSuggestions.add(new SiteSuggestion("0 EXPLORE_SITES", new GURL("https://www.bar.com"),
-                "", TileTitleSource.UNKNOWN, TileSource.EXPLORE, TileSectionType.PERSONALIZED,
-                new Date()));
+        siteSuggestions.add(new SiteSuggestion("0 EXPLORE_SITES",
+                // Use pre-serialized GURL to avoid loading native.
+                JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), "", TileTitleSource.UNKNOWN,
+                TileSource.EXPLORE, TileSectionType.PERSONALIZED, new Date()));
 
+        String urlTemplate = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1_NUMERAL).serialize();
         for (int i = 0; i < 7; i++) {
             siteSuggestions.add(new SiteSuggestion(String.valueOf(i),
-                    new GURL("https://www." + i + ".com"), "", TileTitleSource.TITLE_TAG,
-                    TileSource.TOP_SITES, TileSectionType.PERSONALIZED, new Date()));
+                    // Use pre-serialized GURL to avoid loading native.
+                    GURL.deserialize(urlTemplate.replace("www.1.com", "www." + i + ".com")), "",
+                    TileTitleSource.TITLE_TAG, TileSource.TOP_SITES, TileSectionType.PERSONALIZED,
+                    new Date()));
         }
 
         return siteSuggestions;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediator.java
index eb91cd661b020..48735d91e2f47 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediator.java
@@ -133,8 +133,7 @@ public class SingleTabSwitcherMediator implements TabSwitcher.Controller {
 
     private void updateFavicon(Tab tab) {
         assert mTabListFaviconProvider.isInitialized();
-        // TODO(crbug/783819): convert TabListFaviconProvider to GURL
-        mTabListFaviconProvider.getFaviconForUrlAsync(tab.getUrl().getSpec(), false,
+        mTabListFaviconProvider.getFaviconForUrlAsync(tab.getUrl(), false,
                 (Drawable favicon) -> { mPropertyModel.set(FAVICON, favicon); });
     }
 
@@ -253,7 +252,7 @@ public class SingleTabSwitcherMediator implements TabSwitcher.Controller {
 
     private void updateSelectedTab(Tab tab) {
         mPropertyModel.set(TITLE, tab.getTitle());
-        mTabListFaviconProvider.getFaviconForUrlAsync(tab.getUrl().getSpec(), false,
+        mTabListFaviconProvider.getFaviconForUrlAsync(tab.getUrl(), false,
                 (Drawable favicon) -> { mPropertyModel.set(FAVICON, favicon); });
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
index 39c4dc849e1ab..a2551a0c7a002 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTab.java
@@ -26,6 +26,7 @@ import org.chromium.chrome.browser.tabmodel.TabbedModeTabPersistencePolicy;
 import org.chromium.chrome.browser.tabpersistence.TabStateDirectory;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.components.embedder_support.util.UrlUtilities;
+import org.chromium.url.GURL;
 
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
@@ -179,10 +180,9 @@ public class PseudoTab {
      * Get the URL of the {@link PseudoTab}.
      * @return The URL
      */
-    public String getUrl() {
-        // TODO(crbug/783819): Return the GURL directly.
+    public GURL getUrl() {
         if (mTab != null && mTab.get() != null && mTab.get().isInitialized()) {
-            return mTab.get().getUrl() != null ? mTab.get().getUrl().getSpec() : null;
+            return mTab.get().getUrl();
         }
         assert mTabId != null;
         return TabAttributeCache.getUrl(mTabId);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java
index 86b1932821f75..2d44b0a908a93 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCache.java
@@ -65,8 +65,7 @@ public class TabAttributeCache {
             @Override
             public void onUrlUpdated(Tab tab) {
                 if (tab.isIncognito()) return;
-                String url = tab.getUrl().getSpec();
-                cacheUrl(tab.getId(), url);
+                cacheUrl(tab.getId(), tab.getUrl());
             }
 
             @Override
@@ -107,6 +106,7 @@ public class TabAttributeCache {
                 int id = tab.getId();
                 getSharedPreferences()
                         .edit()
+                        .remove(getDeprecatedUrlKey(id))
                         .remove(getUrlKey(id))
                         .remove(getTitleKey(id))
                         .remove(getRootIdKey(id))
@@ -126,7 +126,7 @@ public class TabAttributeCache {
                         mTabModelSelector.getTabModelFilterProvider().getTabModelFilter(false);
                 for (int i = 0; i < filter.getCount(); i++) {
                     Tab tab = filter.getTabAt(i);
-                    cacheUrl(tab.getId(), tab.getUrl().getSpec());
+                    cacheUrl(tab.getId(), tab.getUrl());
                     cacheTitle(tab.getId(), tab.getTitle());
                     cacheRootId(tab.getId(), CriticalPersistedTabData.from(tab).getRootId());
                     cacheTimestampMillis(
@@ -168,6 +168,11 @@ public class TabAttributeCache {
     }
 
     private static String getUrlKey(int id) {
+        return id + "_gurl";
+    }
+
+    // Legacy from when URL was serialized as raw string.
+    private static String getDeprecatedUrlKey(int id) {
         return id + "_url";
     }
 
@@ -176,13 +181,16 @@ public class TabAttributeCache {
      * @param id The ID of the {@link PseudoTab}.
      * @return The URL
      */
-    public static String getUrl(int id) {
-        return getSharedPreferences().getString(getUrlKey(id), "");
+    public static GURL getUrl(int id) {
+        String url = getSharedPreferences().getString(getUrlKey(id), "");
+        if (!url.isEmpty()) {
+            return GURL.deserialize(url);
+        }
+        return new GURL(getSharedPreferences().getString(getDeprecatedUrlKey(id), ""));
     }
 
-    private static void cacheUrl(int id, String url) {
-        // TODO(crbug/783819): Use GURL directly.
-        getSharedPreferences().edit().putString(getUrlKey(id), url).apply();
+    private static void cacheUrl(int id, GURL url) {
+        getSharedPreferences().edit().putString(getUrlKey(id), url.serialize()).apply();
     }
 
     /**
@@ -190,7 +198,7 @@ public class TabAttributeCache {
      * @param id The ID of the {@link PseudoTab}.
      * @param url The URL
      */
-    static void setUrlForTesting(int id, String url) {
+    static void setUrlForTesting(int id, GURL url) {
         cacheUrl(id, url);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index cbf2cde1e3df6..58abf0d8f80b2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -28,6 +28,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.url.GURL;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -115,7 +116,7 @@ public class MultiThumbnailCardProvider implements TabListMediator.ThumbnailProv
             for (int i = 0; i < 4; i++) {
                 if (mTabs.get(i) != null) {
                     final int index = i;
-                    final String url = mTabs.get(i).getUrl();
+                    final GURL url = mTabs.get(i).getUrl();
                     final boolean isIncognito = mTabs.get(i).isIncognito();
                     // getTabThumbnailWithCallback() might call the callback up to twice,
                     // so use |lastFavicon| to avoid fetching the favicon the second time.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java
index f5a124855a964..21d9c7661815f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java
@@ -23,6 +23,7 @@ import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.ui.base.ViewUtils;
+import org.chromium.url.GURL;
 
 import java.util.List;
 
@@ -153,7 +154,7 @@ public class TabListFaviconProvider {
      * @param faviconCallback The callback that requests for favicon.
      */
     public void getFaviconForUrlAsync(
-            String url, boolean isIncognito, Callback<Drawable> faviconCallback) {
+            GURL url, boolean isIncognito, Callback<Drawable> faviconCallback) {
         if (mFaviconHelper == null || UrlUtilities.isNTPUrl(url)) {
             faviconCallback.onResult(getRoundedChromeDrawable(isIncognito));
         } else {
@@ -175,7 +176,7 @@ public class TabListFaviconProvider {
      * @param icon The favicon that was received.
      * @return The processed favicon.
      */
-    public Drawable getFaviconForUrlSync(String url, boolean isIncognito, Bitmap icon) {
+    public Drawable getFaviconForUrlSync(GURL url, boolean isIncognito, Bitmap icon) {
         if (icon == null) {
             boolean isNativeUrl = NativePage.isNativePageUrl(url, isIncognito);
             return isNativeUrl ? getRoundedChromeDrawable(isIncognito)
@@ -192,7 +193,7 @@ public class TabListFaviconProvider {
      * @param faviconCallback The callback that requests for the composed favicon.
      */
     public void getComposedFaviconImageAsync(
-            List<String> urls, boolean isIncognito, Callback<Drawable> faviconCallback) {
+            List<GURL> urls, boolean isIncognito, Callback<Drawable> faviconCallback) {
         assert urls != null && urls.size() > 1 && urls.size() <= 4;
 
         mFaviconHelper.getComposedFaviconImage(mProfile, urls, mFaviconSize, (image, iconUrl) -> {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 48b13b49ced48..828d1befc000e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -1614,11 +1614,11 @@ class TabListMediator {
             }
 
             // The order of the url list matches the multi-thumbnail.
-            List<String> urls = new ArrayList<>();
+            List<GURL> urls = new ArrayList<>();
             urls.add(pseudoTab.getUrl());
             for (int i = 0; urls.size() < 4 && i < relatedTabList.size(); i++) {
                 if (pseudoTab.getId() == relatedTabList.get(i).getId()) continue;
-                urls.add(relatedTabList.get(i).getUrl().getSpec());
+                urls.add(relatedTabList.get(i).getUrl());
             }
 
             // For tab group card in grid tab switcher, the favicon is the composed favicon.
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediatorUnitTest.java
index 3c1b611b3e149..f14b85f5e2ca5 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/SingleTabSwitcherMediatorUnitTest.java
@@ -44,6 +44,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tasks.tab_management.TabListFaviconProvider;
 import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.url.GURL;
 import org.chromium.url.JUnitTestGURLs;
 
 /** Tests for {@link SingleTabSwitcherMediator}. */
@@ -52,10 +53,10 @@ import org.chromium.url.JUnitTestGURLs;
 public class SingleTabSwitcherMediatorUnitTest {
     private final int mTabId = 1;
     private final String mTitle = "test";
-    private final String mUrlString = JUnitTestGURLs.URL_1;
+    private final GURL mUrl = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1);
     private final int mTabId2 = 2;
     private final String mTitle2 = "test2";
-    private final String mUrlString2 = JUnitTestGURLs.URL_2;
+    private final GURL mUrl2 = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2);
     private SingleTabSwitcherMediator mMediator;
     private PropertyModel mPropertyModel;
 
@@ -99,10 +100,10 @@ public class SingleTabSwitcherMediatorUnitTest {
         doReturn(0).when(mNormalTabModel).index();
         doReturn(1).when(mNormalTabModel).getCount();
         doReturn(false).when(mNormalTabModel).isIncognito();
-        doReturn(JUnitTestGURLs.getGURL(mUrlString)).when(mTab).getUrl();
+        doReturn(mUrl).when(mTab).getUrl();
         doReturn(mTabId).when(mTab).getId();
         doReturn(mTitle).when(mTab).getTitle();
-        doReturn(JUnitTestGURLs.getGURL(mUrlString2)).when(mTab2).getUrl();
+        doReturn(mUrl2).when(mTab2).getUrl();
         doReturn(mTabId2).when(mTab2).getId();
         doReturn(mTitle2).when(mTab2).getTitle();
         doReturn(true).when(mIncognitoTabModel).isIncognito();
@@ -130,7 +131,7 @@ public class SingleTabSwitcherMediatorUnitTest {
                 .addTabModelFilterObserver(mTabModelObserverCaptor.capture());
         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
         verify(mTabListFaviconProvider)
-                .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
+                .getFaviconForUrlAsync(eq(mUrl), eq(false), mFaviconCallbackCaptor.capture());
         assertTrue(mMediator.overviewVisible());
         verify(mOverviewModeObserver).startedShowing();
         verify(mOverviewModeObserver).finishedShowing();
@@ -160,7 +161,7 @@ public class SingleTabSwitcherMediatorUnitTest {
                 .addTabModelFilterObserver(mTabModelObserverCaptor.capture());
         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
         verify(mTabListFaviconProvider)
-                .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
+                .getFaviconForUrlAsync(eq(mUrl), eq(false), mFaviconCallbackCaptor.capture());
         assertTrue(mMediator.overviewVisible());
         verify(mOverviewModeObserver).startedShowing();
         verify(mOverviewModeObserver).finishedShowing();
@@ -199,7 +200,7 @@ public class SingleTabSwitcherMediatorUnitTest {
                 .addTabModelFilterObserver(mTabModelObserverCaptor.capture());
         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
         verify(mTabListFaviconProvider)
-                .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
+                .getFaviconForUrlAsync(eq(mUrl), eq(false), mFaviconCallbackCaptor.capture());
         assertTrue(mMediator.overviewVisible());
         verify(mOverviewModeObserver).startedShowing();
         verify(mOverviewModeObserver).finishedShowing();
@@ -228,7 +229,7 @@ public class SingleTabSwitcherMediatorUnitTest {
                 .addTabModelFilterObserver(mTabModelObserverCaptor.capture());
         verify(mTabModelSelector).addObserver(mTabModelSelectorObserverCaptor.capture());
         verify(mTabListFaviconProvider)
-                .getFaviconForUrlAsync(eq(mUrlString), eq(false), mFaviconCallbackCaptor.capture());
+                .getFaviconForUrlAsync(eq(mUrl), eq(false), mFaviconCallbackCaptor.capture());
         assertEquals(mPropertyModel.get(TITLE), mTitle);
 
         mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java
index 5c3a8900b6d82..4c22b02c0f576 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/PseudoTabUnitTest.java
@@ -32,6 +32,7 @@ import org.chromium.chrome.browser.tasks.tab_management.TabUiUnitTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.url.GURL;
 import org.chromium.url.JUnitTestGURLs;
 
 import java.util.ArrayList;
@@ -250,11 +251,11 @@ public class PseudoTabUnitTest {
 
     @Test
     public void getUrl_real() {
-        String url = JUnitTestGURLs.EXAMPLE_URL;
-        doReturn(JUnitTestGURLs.getGURL(url)).when(mTab1).getUrl();
+        GURL url = JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL);
+        doReturn(url).when(mTab1).getUrl();
 
         PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
-        Assert.assertEquals("", tab.getUrl());
+        Assert.assertEquals(GURL.emptyGURL(), tab.getUrl());
 
         PseudoTab realTab = PseudoTab.fromTab(mTab1);
         Assert.assertNotEquals(tab, realTab);
@@ -263,11 +264,11 @@ public class PseudoTabUnitTest {
 
     @Test
     public void getUrl_cache() {
-        String url = "url 1";
-        TabAttributeCache.setUrlForTesting(TAB1_ID, url);
+        String url = JUnitTestGURLs.URL_1;
+        TabAttributeCache.setUrlForTesting(TAB1_ID, JUnitTestGURLs.getGURL(url));
 
         PseudoTab tab = PseudoTab.fromTabId(TAB1_ID);
-        Assert.assertEquals(url, tab.getUrl());
+        Assert.assertEquals(url, tab.getUrl().getSpec());
 
         PseudoTab realTab = PseudoTab.fromTab(mTab1);
         Assert.assertNotEquals(tab, realTab);
@@ -466,7 +467,7 @@ public class PseudoTabUnitTest {
         // Url was not set. Without the isInitialized() check,
         // pseudoTab.getUrl() would crash here with
         // UnsupportedOperationException
-        Assert.assertEquals("", pseudoTab.getUrl());
+        Assert.assertEquals("", pseudoTab.getUrl().getSpec());
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java
index 600a0e9fe7ceb..7f43c4e647853 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/pseudotab/TabAttributeCacheUnitTest.java
@@ -130,8 +130,8 @@ public class TabAttributeCacheUnitTest {
 
     @Test
     public void updateUrl() {
-        String url = JUnitTestGURLs.EXAMPLE_URL;
-        doReturn(JUnitTestGURLs.getGURL(url)).when(mTab1).getUrl();
+        GURL url = JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL);
+        doReturn(url).when(mTab1).getUrl();
 
         Assert.assertNotEquals(url, TabAttributeCache.getUrl(TAB1_ID));
 
@@ -359,8 +359,8 @@ public class TabAttributeCacheUnitTest {
 
     @Test
     public void onTabStateInitialized() {
-        String url1 = JUnitTestGURLs.EXAMPLE_URL;
-        doReturn(JUnitTestGURLs.getGURL(url1)).when(mTab1).getUrl();
+        GURL url1 = JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL);
+        doReturn(url1).when(mTab1).getUrl();
         String title1 = "title 1";
         doReturn(title1).when(mTab1).getTitle();
         int rootId1 = 1337;
@@ -368,8 +368,8 @@ public class TabAttributeCacheUnitTest {
         long timestamp1 = 123456;
         doReturn(timestamp1).when(mCriticalPersistedTabData1).getTimestampMillis();
 
-        String url2 = JUnitTestGURLs.URL_2;
-        doReturn(JUnitTestGURLs.getGURL(url2)).when(mTab2).getUrl();
+        GURL url2 = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2);
+        doReturn(url2).when(mTab2).getUrl();
         String title2 = "title 2";
         doReturn(title2).when(mTab2).getTitle();
         int rootId2 = 42;
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtilsUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtilsUnitTest.java
index 1d05f8bfd492a..65bf79497afa1 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtilsUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_groups/TabGroupUtilsUnitTest.java
@@ -36,6 +36,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiUnitTestUtils;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.chromium.url.GURL;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -90,9 +91,9 @@ public class TabGroupUtilsUnitTest {
 
         MockitoAnnotations.initMocks(this);
 
-        mTab1 = TabUiUnitTestUtils.prepareTab(TAB1_ID, TAB1_TITLE, "");
-        mTab2 = TabUiUnitTestUtils.prepareTab(TAB2_ID, TAB2_TITLE, "");
-        mTab3 = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE, "");
+        mTab1 = TabUiUnitTestUtils.prepareTab(TAB1_ID, TAB1_TITLE, GURL.emptyGURL());
+        mTab2 = TabUiUnitTestUtils.prepareTab(TAB2_ID, TAB2_TITLE, GURL.emptyGURL());
+        mTab3 = TabUiUnitTestUtils.prepareTab(TAB3_ID, TAB3_TITLE, GURL.emptyGURL());
 
         doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
         doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index 27383bef30454..a8f8bd59996e3 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -64,6 +64,7 @@ import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.url.GURL;
 import org.chromium.url.JUnitTestGURLs;
 
 import java.util.ArrayList;
@@ -1072,7 +1073,7 @@ public class TabGridDialogMediatorUnitTest {
     }
 
     private TabImpl prepareTab(int id, String title) {
-        TabImpl tab = TabUiUnitTestUtils.prepareTab(id, title, "");
+        TabImpl tab = TabUiUnitTestUtils.prepareTab(id, title, GURL.emptyGURL());
         doReturn(true).when(tab).isIncognito();
         return tab;
     }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 21c34192225ad..5c9116b9b3076 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -19,6 +19,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
@@ -177,9 +178,9 @@ public class TabListMediatorUnitTest {
     private static final String NEW_TITLE = "New title";
     private static final String CUSTOMIZED_DIALOG_TITLE1 = "Cool Tabs";
     private static final String TAB_GROUP_TITLES_FILE_NAME = "tab_group_titles";
-    private static final String TAB1_URL = JUnitTestGURLs.URL_1;
-    private static final String TAB2_URL = JUnitTestGURLs.URL_2;
-    private static final String TAB3_URL = JUnitTestGURLs.URL_3;
+    private static final GURL TAB1_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_1);
+    private static final GURL TAB2_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_2);
+    private static final GURL TAB3_URL = JUnitTestGURLs.getGURL(JUnitTestGURLs.URL_3);
     private static final String NEW_URL = JUnitTestGURLs.EXAMPLE_URL;
     private static final int TAB1_ID = 456;
     private static final int TAB2_ID = 789;
@@ -315,9 +316,9 @@ public class TabListMediatorUnitTest {
         // Ensure native pointer is initialized
         doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
 
-        mTab1Domain = JUnitTestGURLs.getGURL(TAB1_URL).getHost().replace("www.", "");
-        mTab2Domain = JUnitTestGURLs.getGURL(TAB2_URL).getHost().replace("www.", "");
-        mTab3Domain = JUnitTestGURLs.getGURL(TAB3_URL).getHost().replace("www.", "");
+        mTab1Domain = TAB1_URL.getHost().replace("www.", "");
+        mTab2Domain = TAB2_URL.getHost().replace("www.", "");
+        mTab3Domain = TAB3_URL.getHost().replace("www.", "");
         mNewDomain = JUnitTestGURLs.getGURL(NEW_URL).getHost().replace("www.", "");
 
         CachedFeatureFlags.setForTesting(ChromeFeatureList.START_SURFACE_ANDROID, false);
@@ -356,10 +357,10 @@ public class TabListMediatorUnitTest {
         doReturn(2).when(mTabModel).getCount();
         doNothing()
                 .when(mTabListFaviconProvider)
-                .getFaviconForUrlAsync(anyString(), anyBoolean(), mCallbackCaptor.capture());
+                .getFaviconForUrlAsync(anyObject(), anyBoolean(), mCallbackCaptor.capture());
         doReturn(mFaviconDrawable)
                 .when(mTabListFaviconProvider)
-                .getFaviconForUrlSync(anyString(), anyBoolean(), any(Bitmap.class));
+                .getFaviconForUrlSync(anyObject(), anyBoolean(), any(Bitmap.class));
         doReturn(mTab1).when(mTabModelSelector).getTabById(TAB1_ID);
         doReturn(mTab2).when(mTabModelSelector).getTabById(TAB2_ID);
         doReturn(tabs1).when(mTabGroupModelFilter).getRelatedTabList(TAB1_ID);
@@ -379,13 +380,13 @@ public class TabListMediatorUnitTest {
         doReturn(mSpanSizeLookup).when(mGridLayoutManager).getSpanSizeLookup();
         doReturn(mTab1Domain)
                 .when(mUrlUtilitiesJniMock)
-                .getDomainAndRegistry(eq(TAB1_URL), anyBoolean());
+                .getDomainAndRegistry(eq(TAB1_URL.getSpec()), anyBoolean());
         doReturn(mTab2Domain)
                 .when(mUrlUtilitiesJniMock)
-                .getDomainAndRegistry(eq(TAB2_URL), anyBoolean());
+                .getDomainAndRegistry(eq(TAB2_URL.getSpec()), anyBoolean());
         doReturn(mTab3Domain)
                 .when(mUrlUtilitiesJniMock)
-                .getDomainAndRegistry(eq(TAB3_URL), anyBoolean());
+                .getDomainAndRegistry(eq(TAB3_URL.getSpec()), anyBoolean());
         doNothing().when(mTemplateUrlService).addObserver(mTemplateUrlServiceObserver.capture());
         doReturn(true).when(mTabListFaviconProvider).isInitialized();
 
@@ -1919,10 +1920,8 @@ public class TabListMediatorUnitTest {
                             PriceTrackingUtilities.TRACK_PRICES_ON_TABS, priceTrackingEnabled);
                     Profile.setLastUsedProfileForTesting(mProfile);
                     Map<GURL, Any> responses = new HashMap<>();
-                    GURL gurl1 = JUnitTestGURLs.getGURL(TAB1_URL);
-                    GURL gurl2 = JUnitTestGURLs.getGURL(TAB2_URL);
-                    responses.put(gurl1, ANY_BUYABLE_PRODUCT_INITIAL);
-                    responses.put(gurl2, ANY_EMPTY);
+                    responses.put(TAB1_URL, ANY_BUYABLE_PRODUCT_INITIAL);
+                    responses.put(TAB2_URL, ANY_EMPTY);
                     mockOptimizationGuideResponse(OptimizationGuideDecision.TRUE, responses);
                     PersistedTabDataConfiguration.setUseTestConfig(true);
                     initAndAssertAllProperties(mMediatorSpy);
@@ -2436,7 +2435,7 @@ public class TabListMediatorUnitTest {
         List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2, tab3));
         createTabGroup(tabs, TAB1_ID);
         mTabObserverCaptor.getValue().onFaviconUpdated(mTab1, mFaviconBitmap);
-        List<String> urls = new ArrayList<>(Arrays.asList(TAB1_URL, TAB2_URL, TAB3_URL));
+        List<GURL> urls = new ArrayList<>(Arrays.asList(TAB1_URL, TAB2_URL, TAB3_URL));
         verify(mTabListFaviconProvider).getComposedFaviconImageAsync(eq(urls), anyBoolean(), any());
         mCallbackCaptor.getValue().onResult(mFaviconDrawable);
         assertThat(mModel.get(0).model.get(TabProperties.FAVICON), equalTo(mFaviconDrawable));
@@ -2444,11 +2443,12 @@ public class TabListMediatorUnitTest {
         // Test a group of five.
         mModel.get(1).model.set(TabProperties.FAVICON, null);
         TabImpl tab4 = prepareTab(0, "tab 4", TAB2_URL);
-        TabImpl tab5 = prepareTab(1, "tab 5", "www.tab5.com");
+        TabImpl tab5 = prepareTab(1, "tab 5", JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL));
         tabs.addAll(Arrays.asList(tab4, tab5));
         createTabGroup(tabs, TAB2_ID);
         mTabObserverCaptor.getValue().onFaviconUpdated(mTab2, mFaviconBitmap);
         urls = new ArrayList<>(Arrays.asList(TAB2_URL, TAB1_URL, TAB3_URL, TAB2_URL));
+
         verify(mTabListFaviconProvider).getComposedFaviconImageAsync(eq(urls), anyBoolean(), any());
         mCallbackCaptor.getValue().onResult(mFaviconDrawable);
         assertThat(mModel.get(1).model.get(TabProperties.FAVICON), equalTo(mFaviconDrawable));
@@ -2704,7 +2704,7 @@ public class TabListMediatorUnitTest {
                 instanceOf(TabListMediator.TabActionListener.class));
     }
 
-    private TabImpl prepareTab(int id, String title, String url) {
+    private TabImpl prepareTab(int id, String title, GURL url) {
         TabImpl tab = TabUiUnitTestUtils.prepareTab(id, title, url);
         when(tab.getView()).thenReturn(mock(View.class));
         doReturn(true).when(tab).isIncognito();
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java
index 30844ef266077..2974cb4ef9a77 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUnitTestUtils.java
@@ -53,17 +53,13 @@ public class TabUiUnitTestUtils {
         doReturn(userDataHost).when(tab).getUserDataHost();
     }
 
-    public static TabImpl prepareTab(int id, String title, String urlString) {
+    public static TabImpl prepareTab(int id, String title, GURL url) {
         CriticalPersistedTabData criticalPersistedTabData = mock(CriticalPersistedTabData.class);
         TabImpl tab = prepareTab(id, criticalPersistedTabData);
         doReturn(id).when(criticalPersistedTabData).getRootId();
         doReturn(title).when(tab).getTitle();
-
-        // TODO(crbug/783819): don't mock GURL here.
-        GURL gurl = mock(GURL.class);
-        doReturn(urlString).when(gurl).getSpec();
-        doReturn(gurl).when(tab).getOriginalUrl();
-        doReturn(gurl).when(tab).getUrl();
+        doReturn(url).when(tab).getOriginalUrl();
+        doReturn(url).when(tab).getUrl();
         return tab;
     }
 
diff --git a/chrome/browser/android/favicon_helper.cc b/chrome/browser/android/favicon_helper.cc
index ce6c2717f5865..49569362ce0c8 100644
--- a/chrome/browser/android/favicon_helper.cc
+++ b/chrome/browser/android/favicon_helper.cc
@@ -58,7 +58,7 @@ class FaviconHelper::Job {
  public:
   Job(FaviconHelper* favicon_helper,
       favicon::FaviconService* favicon_service,
-      std::vector<std::string> urls,
+      std::vector<GURL> urls,
       int desire_size_in_pixel,
       JobFinishedCallback job_finished_callback,
       favicon_base::FaviconResultsCallback result_callback);
@@ -73,7 +73,7 @@ class FaviconHelper::Job {
                           const favicon_base::FaviconRawBitmapResult& result);
   FaviconHelper* favicon_helper_;
   favicon::FaviconService* favicon_service_;
-  std::vector<std::string> urls_;
+  std::vector<GURL> urls_;
   int desire_size_in_pixel_;
   JobFinishedCallback job_finished_callback_;
   favicon_base::FaviconResultsCallback result_callback_;
@@ -86,7 +86,7 @@ class FaviconHelper::Job {
 
 FaviconHelper::Job::Job(FaviconHelper* favicon_helper,
                         favicon::FaviconService* favicon_service,
-                        std::vector<std::string> urls,
+                        std::vector<GURL> urls,
                         int desire_size_in_pixel,
                         JobFinishedCallback job_finished_callback,
                         favicon_base::FaviconResultsCallback result_callback)
@@ -113,7 +113,7 @@ void FaviconHelper::Job::Start() {
                        weak_ptr_factory_.GetWeakPtr(), i);
 
     favicon_helper_->GetLocalFaviconImageForURLInternal(
-        favicon_service_, GURL(urls_.at(i)), desire_size_in_pixel_,
+        favicon_service_, urls_.at(i), desire_size_in_pixel_,
         std::move(callback));
   }
 }
@@ -180,8 +180,8 @@ jboolean FaviconHelper::GetComposedFaviconImage(
                      ScopedJavaGlobalRef<jobject>(j_favicon_image_callback),
                      desired_size_in_pixel);
 
-  std::vector<std::string> urls;
-  base::android::AppendJavaStringArrayToStringVector(env, j_urls, &urls);
+  std::vector<GURL> urls;
+  url::GURLAndroid::JavaGURLArrayToGURLVector(env, j_urls, &urls);
 
   GetComposedFaviconImageInternal(favicon_service, urls,
                                   static_cast<int>(j_desired_size_in_pixel),
@@ -192,7 +192,7 @@ jboolean FaviconHelper::GetComposedFaviconImage(
 
 void FaviconHelper::GetComposedFaviconImageInternal(
     favicon::FaviconService* favicon_service,
-    std::vector<std::string> urls,
+    std::vector<GURL> urls,
     int desired_size_in_pixel,
     favicon_base::FaviconResultsCallback callback_runner) {
   DCHECK(favicon_service);
diff --git a/chrome/browser/android/favicon_helper.h b/chrome/browser/android/favicon_helper.h
index cf3b992461305..63f3d0e273d7a 100644
--- a/chrome/browser/android/favicon_helper.h
+++ b/chrome/browser/android/favicon_helper.h
@@ -48,7 +48,7 @@ class FaviconHelper {
       favicon_base::FaviconRawBitmapCallback callback_runner);
   void GetComposedFaviconImageInternal(
       favicon::FaviconService* favicon_service,
-      std::vector<std::string> urls,
+      std::vector<GURL> urls,
       int desired_size_in_pixel,
       favicon_base::FaviconResultsCallback callback_runner);
   void OnJobFinished(int job_id);
diff --git a/chrome/browser/android/favicon_helper_unittest.cc b/chrome/browser/android/favicon_helper_unittest.cc
index e86be600d87a2..b32cf4e94094f 100644
--- a/chrome/browser/android/favicon_helper_unittest.cc
+++ b/chrome/browser/android/favicon_helper_unittest.cc
@@ -89,10 +89,9 @@ TEST_F(FaviconHelperTest, GetLargestSizeIndex) {
 
 TEST_F(FaviconHelperTest, GetComposedFaviconImage) {
   raw_bitmap_results_.clear();
-  std::vector<std::string> urls = {"http://www.tab1.com",
-                                   "http://www.tab2.com"};
-  GURL url1 = GURL(urls[0]);
-  GURL url2 = GURL(urls[1]);
+  GURL url1 = GURL("http://www.tab1.com");
+  GURL url2 = GURL("http://www.tab2.com");
+  std::vector<GURL> urls = {url1, url2};
 
   EXPECT_CALL(mock_favicon_service_,
               GetRawFaviconForPageURL(url1, _, _, 16, _, _))
@@ -112,11 +111,10 @@ TEST_F(FaviconHelperTest, GetComposedFaviconImage) {
 
 TEST_F(FaviconHelperTest, GetComposedFaviconImageWithOneFaviconFailed) {
   raw_bitmap_results_.clear();
-  std::vector<std::string> urls = {"http://www.tab1.com", "http://www.tab2.com",
-                                   "http://www.tab3.com"};
-  GURL url1 = GURL(urls[0]);
-  GURL url2 = GURL(urls[1]);
-  GURL url3 = GURL(urls[2]);
+  GURL url1 = GURL("http://www.tab1.com");
+  GURL url2 = GURL("http://www.tab2.com");
+  GURL url3 = GURL("http://www.tab3.com");
+  std::vector<GURL> urls = {url1, url2, url3};
 
   EXPECT_CALL(mock_favicon_service_,
               GetRawFaviconForPageURL(url1, _, _, 16, _, _))
@@ -144,11 +142,10 @@ TEST_F(FaviconHelperTest, GetComposedFaviconImageWithOneFaviconFailed) {
 
 TEST_F(FaviconHelperTest, GetComposedFaviconImageOrderMatchesInput) {
   raw_bitmap_results_.clear();
-  std::vector<std::string> urls = {"http://www.tab1.com", "http://www.tab2.com",
-                                   "http://www.tab3.com"};
-  GURL url1 = GURL(urls[0]);
-  GURL url2 = GURL(urls[1]);
-  GURL url3 = GURL(urls[2]);
+  GURL url1 = GURL("http://www.tab1.com");
+  GURL url2 = GURL("http://www.tab2.com");
+  GURL url3 = GURL("http://www.tab3.com");
+  std::vector<GURL> urls = {url1, url2, url3};
 
   EXPECT_CALL(mock_favicon_service_,
               GetRawFaviconForPageURL(url1, _, _, 16, _, _))
diff --git a/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconHelper.java b/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconHelper.java
index f43b21bfe5e9e..6e425a61ee61b 100644
--- a/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconHelper.java
+++ b/chrome/browser/ui/android/favicon/java/src/org/chromium/chrome/browser/ui/favicon/FaviconHelper.java
@@ -191,7 +191,7 @@ public class FaviconHelper {
      *         that this callback is not called if this method returns false.
      * @return True if GetLocalFaviconImageForURL is successfully called.
      */
-    public boolean getComposedFaviconImage(Profile profile, @NonNull List<String> urls,
+    public boolean getComposedFaviconImage(Profile profile, @NonNull List<GURL> urls,
             int desiredSizeInPixel, FaviconImageCallback faviconImageCallback) {
         assert mNativeFaviconHelper != 0;
 
@@ -201,14 +201,14 @@ public class FaviconHelper {
         }
 
         return FaviconHelperJni.get().getComposedFaviconImage(mNativeFaviconHelper, profile,
-                urls.toArray(new String[0]), desiredSizeInPixel, faviconImageCallback);
+                urls.toArray(new GURL[0]), desiredSizeInPixel, faviconImageCallback);
     }
 
     @NativeMethods
     interface Natives {
         long init();
         void destroy(long nativeFaviconHelper);
-        boolean getComposedFaviconImage(long nativeFaviconHelper, Profile profile, String[] urls,
+        boolean getComposedFaviconImage(long nativeFaviconHelper, Profile profile, GURL[] urls,
                 int desiredSizeInDip, FaviconImageCallback faviconImageCallback);
         boolean getLocalFaviconImageForURL(long nativeFaviconHelper, Profile profile,
                 String pageUrl, int desiredSizeInDip, FaviconImageCallback faviconImageCallback);
diff --git a/url/android/gurl_android.cc b/url/android/gurl_android.cc
index 861bc6f8f3f0f..a16d2b8826573 100644
--- a/url/android/gurl_android.cc
+++ b/url/android/gurl_android.cc
@@ -57,6 +57,17 @@ static void InitFromGURL(JNIEnv* env,
                                     gurl.parsed_for_possibly_invalid_spec()));
 }
 
+// As |GetArrayLength| makes no guarantees about the returned value (e.g., it
+// may be -1 if |array| is not a valid Java array), provide a safe wrapper
+// that always returns a valid, non-negative size.
+template <typename JavaArrayType>
+size_t SafeGetArrayLength(JNIEnv* env, const JavaRef<JavaArrayType>& jarray) {
+  DCHECK(jarray);
+  jsize length = env->GetArrayLength(jarray.obj());
+  DCHECK_GE(length, 0) << "Invalid array length: " << length;
+  return static_cast<size_t>(std::max(0, length));
+}
+
 }  // namespace
 
 // static
@@ -67,6 +78,23 @@ std::unique_ptr<GURL> GURLAndroid::ToNativeGURL(
       reinterpret_cast<GURL*>(Java_GURL_toNativeGURL(env, j_gurl)));
 }
 
+void GURLAndroid::JavaGURLArrayToGURLVector(
+    JNIEnv* env,
+    const base::android::JavaRef<jobjectArray>& array,
+    std::vector<GURL>* out) {
+  DCHECK(out);
+  DCHECK(out->empty());
+  if (!array)
+    return;
+  size_t len = SafeGetArrayLength(env, array);
+  for (size_t i = 0; i < len; ++i) {
+    ScopedJavaLocalRef<jobject> j_gurl(
+        env, static_cast<jobject>(env->GetObjectArrayElement(array.obj(), i)));
+    out->emplace_back(
+        *reinterpret_cast<GURL*>(Java_GURL_toNativeGURL(env, j_gurl)));
+  }
+}
+
 // static
 ScopedJavaLocalRef<jobject> GURLAndroid::FromNativeGURL(JNIEnv* env,
                                                         const GURL& gurl) {
diff --git a/url/android/gurl_android.h b/url/android/gurl_android.h
index b36538df593dd..b0e3b729b7d68 100644
--- a/url/android/gurl_android.h
+++ b/url/android/gurl_android.h
@@ -25,6 +25,10 @@ class GURLAndroid {
   static base::android::ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfGURLs(
       JNIEnv* env,
       base::span<base::android::ScopedJavaLocalRef<jobject>> v);
+  static void JavaGURLArrayToGURLVector(
+      JNIEnv* env,
+      const base::android::JavaRef<jobjectArray>& gurl_array,
+      std::vector<GURL>* out);
 };
 
 }  // namespace url
diff --git a/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java b/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java
index 93d2992caca2b..f6b68327bccbd 100644
--- a/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java
+++ b/url/android/test/java/src/org/chromium/url/JUnitTestGURLs.java
@@ -25,6 +25,7 @@ public class JUnitTestGURLs {
     //    in the map.
     public static final String EXAMPLE_URL = "https://www.example.com/";
     public static final String URL_1 = "https://www.one.com/";
+    public static final String URL_1_NUMERAL = "https://www.1.com/";
     public static final String URL_1_WITH_PATH = "https://www.one.com/some_path.html";
     public static final String URL_2 = "https://www.two.com/";
     public static final String URL_3 = "https://www.three.com/";
@@ -56,6 +57,9 @@ public class JUnitTestGURLs {
         map.put(URL_1,
                 "78,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,1,0,-1,0,-1,"
                         + "false,false,https://www.one.com/");
+        map.put(URL_1_NUMERAL,
+                "75,1,true,0,5,0,-1,0,-1,8,9,0,-1,17,1,0,-1,0,-1,"
+                        + "false,false,https://www.1.com/");
         map.put(URL_1_WITH_PATH,
                 "93,1,true,0,5,0,-1,0,-1,8,11,0,-1,19,15,0,-1,0,-1,"
                         + "false,false,https://www.one.com/some_path.html");