[TabGroupDragDrop] Allow dragging groups out of strip

Allow dragging groups out of strip.
-Populates the ChromeDropDataAndroid.
-Intent not currently handled on drop. Likely need to add separate MIME
 type (or perhaps even DropData type) to handle separately.
-Temporarily uses first tab's thumbnail for the drag shadow.

Video: https://screencast.googleplex.com/cast/NTQyNTg5ODA4MDY5ODM2OHxiY2NiMDE0Ny02Mw

Bug: 380327012
Change-Id: I218b8603306b3a04ad059d32254b597e58c3fec2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6307702
Reviewed-by: Zhe Li <zheliooo@google.com>
Commit-Queue: Neil Coronado <nemco@google.com>
Reviewed-by: Sirisha Kavuluru <skavuluru@google.com>
Reviewed-by: Shu Yang <shuyng@google.com>
Cr-Commit-Position: refs/heads/main@{#1427940}
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 2025e55..cd92a41a 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -562,6 +562,8 @@
   "java/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegate.java",
   "java/src/org/chromium/chrome/browser/dragdrop/ChromeDragDropUtils.java",
   "java/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroid.java",
+  "java/src/org/chromium/chrome/browser/dragdrop/ChromeTabDropDataAndroid.java",
+  "java/src/org/chromium/chrome/browser/dragdrop/ChromeTabGroupDropDataAndroid.java",
   "java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java",
   "java/src/org/chromium/chrome/browser/dragdrop/DragAndDropLauncherActivity.java",
   "java/src/org/chromium/chrome/browser/externalnav/ExternalNavigationDelegateImpl.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index 6296c28..a16a758 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -56,11 +56,11 @@
     /** Returns whether drag drop from tab strip to create new instance is enabled. */
     public static boolean isTabDragToCreateInstanceSupported() {
         // TODO(crbug/328511660): Add OS version check once available.
-        return doesOEMSupportDragToCreateInstance() || !isTabDragAsWindowEnabled();
+        return doesOemSupportDragToCreateInstance() || !isTabDragAsWindowEnabled();
     }
 
     /** Returns whether device OEM is allow-listed for tab tearing */
-    public static boolean doesOEMSupportDragToCreateInstance() {
+    public static boolean doesOemSupportDragToCreateInstance() {
         return TAB_TEARING_OEM_ALLOWLIST.contains(Build.MANUFACTURER.toLowerCase(Locale.US));
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java
index daaa499..1cddf05 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSource.java
@@ -43,10 +43,14 @@
 import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelper;
 import org.chromium.chrome.browser.dragdrop.ChromeDragDropUtils;
 import org.chromium.chrome.browser.dragdrop.ChromeDropDataAndroid;
+import org.chromium.chrome.browser.dragdrop.ChromeTabDropDataAndroid;
+import org.chromium.chrome.browser.dragdrop.ChromeTabGroupDropDataAndroid;
 import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
+import org.chromium.chrome.browser.tabmodel.TabGroupMetadata;
+import org.chromium.chrome.browser.tabmodel.TabGroupMetadataExtractor;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
@@ -62,6 +66,8 @@
 import org.chromium.ui.dragdrop.DragDropMetricUtils.DragDropType;
 import org.chromium.ui.widget.Toast;
 
+import java.util.List;
+
 /**
  * Manages initiating tab drag and drop and handles the events that are received during drag and
  * drop process. The tab drag and drop is initiated from the active instance of {@link
@@ -172,7 +178,7 @@
             float tabPositionX,
             float tabWidthDp) {
         // Return false when another drag in progress.
-        if (DragDropGlobalState.hasValue()) {
+        if (isDragAlreadyInProgress()) {
             return false;
         }
 
@@ -187,14 +193,10 @@
             return false;
         }
 
-        if (sDragTrackerToken != null) {
-            Log.w(TAG, "Attempting to start drag before clearing state from prior drag");
-        }
-
         // Allow drag to create new instance based on feature checks / current instance count.
         boolean allowDragToCreateInstance =
                 shouldAllowTabDragToCreateInstance()
-                        && (TabUiFeatureUtilities.doesOEMSupportDragToCreateInstance()
+                        && (TabUiFeatureUtilities.doesOemSupportDragToCreateInstance()
                                 || MultiWindowUtils.getInstanceCount()
                                         < MultiWindowUtils.getMaxInstances());
 
@@ -205,7 +207,7 @@
 
         // Build shared state with all info.
         ChromeDropDataAndroid dropData =
-                new ChromeDropDataAndroid.Builder()
+                new ChromeTabDropDataAndroid.Builder()
                         .withTab(tabBeingDragged)
                         .withTabInGroup(isTabInGroup)
                         .withAllowDragToCreateInstance(allowDragToCreateInstance)
@@ -214,15 +216,7 @@
         updateShadowView(tabBeingDragged, dragSourceView, (int) (tabWidthDp / mPxToDp));
         DragShadowBuilder builder =
                 createDragShadowBuilder(dragSourceView, startPoint, tabPositionX);
-        sDragTrackerToken =
-                DragDropGlobalState.store(
-                        mMultiInstanceManager.getCurrentInstanceId(), dropData, builder);
-        boolean res = mDragAndDropDelegate.startDragAndDrop(dragSourceView, builder, dropData);
-        if (!res) {
-            DragDropGlobalState.clear(sDragTrackerToken);
-            sDragTrackerToken = null;
-        }
-        return res;
+        return startDragAction(dropData, builder, dragSourceView);
     }
 
     /**
@@ -241,8 +235,68 @@
             @NonNull PointF startPoint,
             float positionX,
             float widthDp) {
-        // TODO(crbug.com/384969886): Implement.
-        return false;
+        // Return false when another drag in progress.
+        if (isDragAlreadyInProgress()) {
+            return false;
+        }
+
+        // Block drag for last group in single-window mode if feature is not supported.
+        boolean allowDragToCreateInstance = shouldAllowGroupDragToCreateInstance(tabGroupId);
+        if (!MultiWindowUtils.getInstance().isInMultiWindowMode(getActivity())
+                && !allowDragToCreateInstance) {
+            return false;
+        }
+
+        // TODO(crbug.com/380327012): Block drag for last group when homepage enabled and is set to
+        //  a custom url.
+
+        // Allow drag to create new instance based on feature checks / current instance count.
+        allowDragToCreateInstance =
+                allowDragToCreateInstance
+                        && (MultiWindowUtils.getInstanceCount()
+                                < MultiWindowUtils.getMaxInstances());
+
+        // Build shared state with all info.
+        TabGroupModelFilter tabGroupModelFilter =
+                mTabModelSelector.getTabGroupModelFilterProvider().getCurrentTabGroupModelFilter();
+        int rootId = tabGroupModelFilter.getRootIdFromTabGroupId(tabGroupId);
+        List<Tab> groupedTabs = tabGroupModelFilter.getRelatedTabListForRootId(rootId);
+        int windowId = TabWindowManagerSingleton.getInstance().getIndexForWindow(getActivity());
+
+        TabGroupMetadata metadata =
+                TabGroupMetadataExtractor.extractTabGroupMetadata(
+                        groupedTabs, windowId, mTabModelSelector.getCurrentTabId());
+        ChromeDropDataAndroid dropData =
+                new ChromeTabGroupDropDataAndroid.Builder()
+                        .withTabGroupMetadata(metadata)
+                        .withAllowDragToCreateInstance(allowDragToCreateInstance)
+                        .build();
+
+        // TODO(crbug.com/383124686): Update drag shadow to use group thumbnail.
+        updateShadowView(groupedTabs.get(0), dragSourceView, (int) (widthDp / mPxToDp));
+        DragShadowBuilder builder = createDragShadowBuilder(dragSourceView, startPoint, positionX);
+        return startDragAction(dropData, builder, dragSourceView);
+    }
+
+    private boolean startDragAction(
+            ChromeDropDataAndroid dropData, DragShadowBuilder builder, View dragSourceView) {
+        sDragTrackerToken =
+                DragDropGlobalState.store(
+                        mMultiInstanceManager.getCurrentInstanceId(), dropData, builder);
+        boolean res = mDragAndDropDelegate.startDragAndDrop(dragSourceView, builder, dropData);
+        if (!res) {
+            DragDropGlobalState.clear(sDragTrackerToken);
+            sDragTrackerToken = null;
+        }
+        return res;
+    }
+
+    private boolean isDragAlreadyInProgress() {
+        if (sDragTrackerToken != null) {
+            Log.w(TAG, "Attempting to start drag before clearing state from prior drag");
+        }
+
+        return DragDropGlobalState.hasValue();
     }
 
     @VisibleForTesting
@@ -388,7 +442,7 @@
         }
         mStripLayoutHelperSupplier
                 .get()
-                .handleDragEnter(xPx * mPxToDp, mLastXDp, isDragSource, isDraggedTabIncognito());
+                .handleDragEnter(xPx * mPxToDp, mLastXDp, isDragSource, isDraggedItemIncognito());
         return true;
     }
 
@@ -402,7 +456,7 @@
                         xDp,
                         yDp,
                         xDp - mLastXDp,
-                        isDraggedTabIncognito());
+                        isDraggedItemIncognito());
         return true;
     }
 
@@ -525,8 +579,8 @@
 
     private void recordTabRemovedFromGroupUserAction() {
         DragDropGlobalState globalState = DragDropGlobalState.getState(sDragTrackerToken);
-        if (globalState.getData() instanceof ChromeDropDataAndroid
-                && ((ChromeDropDataAndroid) globalState.getData()).isTabInGroup) {
+        if (globalState.getData() instanceof ChromeTabDropDataAndroid
+                && ((ChromeTabDropDataAndroid) globalState.getData()).isTabInGroup) {
             RecordUserAction.record("MobileToolbarReorderTab.TabRemovedFromGroup");
         }
     }
@@ -547,7 +601,7 @@
                 mShadowView.expand();
             }
         }
-        mStripLayoutHelperSupplier.get().handleDragExit(isDragSource(), isDraggedTabIncognito());
+        mStripLayoutHelperSupplier.get().handleDragExit(isDragSource(), isDraggedItemIncognito());
         return true;
     }
 
@@ -570,9 +624,8 @@
                         : DragDropGlobalState.getState(sDragTrackerToken);
         // We should only attempt to access this while we know there's an active drag.
         assert globalState != null : "Attempting to access dragged tab with invalid drag state.";
-        assert globalState.getData() instanceof ChromeDropDataAndroid
-                : "Attempting to access dragged tab with wrong data type";
-        return ((ChromeDropDataAndroid) globalState.getData()).tab;
+        if (!(globalState.getData() instanceof ChromeTabDropDataAndroid)) return null;
+        return ((ChromeTabDropDataAndroid) globalState.getData()).tab;
     }
 
     private boolean isDragSource() {
@@ -582,8 +635,14 @@
         return globalState.isDragSourceInstance(mMultiInstanceManager.getCurrentInstanceId());
     }
 
-    private boolean isDraggedTabIncognito() {
-        return getTabFromGlobalState(null).isIncognito();
+    private boolean isDraggedItemIncognito() {
+        DragDropGlobalState globalState = DragDropGlobalState.getState(sDragTrackerToken);
+        assert globalState != null;
+
+        ChromeDropDataAndroid dropData = (ChromeDropDataAndroid) globalState.getData();
+        assert dropData != null;
+
+        return dropData.isIncognito();
     }
 
     /**
@@ -788,6 +847,16 @@
         return new PointF(positionXOnScreen, positionYOnScreen);
     }
 
+    private boolean shouldAllowGroupDragToCreateInstance(Token groupId) {
+        TabGroupModelFilter filter =
+                mTabModelSelector.getTabGroupModelFilterProvider().getCurrentTabGroupModelFilter();
+        int rootId = filter.getRootIdFromTabGroupId(groupId);
+        int groupSize = filter.getRelatedTabCountForRootId(rootId);
+
+        return mTabModelSelector.getTotalTabCount() > groupSize
+                && TabUiFeatureUtilities.doesOemSupportDragToCreateInstance();
+    }
+
     private boolean shouldAllowTabDragToCreateInstance() {
         return hasMultipleTabs(mTabModelSelector)
                 && TabUiFeatureUtilities.isTabDragToCreateInstanceSupported();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegate.java
index d46721c..5fd439b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegate.java
@@ -45,6 +45,7 @@
     private static boolean sDefinedItemWithPendingIntentForTesting;
     private static boolean sClipDataItemBuilderNotFound;
 
+    // TODO(crbug.com/380327012): Add separate MIME type for groups.
     private final String[] mSupportedMimeTypes =
             new String[] {
                 MimeTypeUtils.CHROME_MIMETYPE_TAB,
@@ -119,14 +120,19 @@
     public ClipData buildClipData(@NonNull DropDataAndroid dropData) {
         assert dropData instanceof ChromeDropDataAndroid;
         ChromeDropDataAndroid chromeDropDataAndroid = (ChromeDropDataAndroid) dropData;
-        if (chromeDropDataAndroid.hasTab() && chromeDropDataAndroid.allowTabDragToCreateInstance) {
-            ClipData clipData =
-                    buildClipDataForTabTearing(
-                            chromeDropDataAndroid.tab, chromeDropDataAndroid.windowId);
+        if (chromeDropDataAndroid.hasBrowserContent()
+                && chromeDropDataAndroid.allowDragToCreateInstance) {
+            ClipData clipData = null;
+            if (chromeDropDataAndroid instanceof ChromeTabDropDataAndroid) {
+                clipData =
+                        buildClipDataForTabTearing(
+                                ((ChromeTabDropDataAndroid) chromeDropDataAndroid).tab,
+                                chromeDropDataAndroid.windowId);
+            }
             if (clipData != null) return clipData;
         }
         String text =
-                chromeDropDataAndroid.hasTab()
+                chromeDropDataAndroid.hasBrowserContent()
                         ? chromeDropDataAndroid.buildTabClipDataText()
                         : dropData.text;
         return new ClipData(null, mSupportedMimeTypes, new Item(text));
@@ -161,7 +167,7 @@
     public int buildFlags(int originalFlag, DropDataAndroid dropData) {
         assert dropData instanceof ChromeDropDataAndroid;
         ChromeDropDataAndroid chromeDropData = (ChromeDropDataAndroid) dropData;
-        if (!chromeDropData.hasTab() || !chromeDropData.allowTabDragToCreateInstance) {
+        if (!chromeDropData.hasBrowserContent() || !chromeDropData.allowDragToCreateInstance) {
             return originalFlag;
         }
         return originalFlag
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroid.java
index 73d3430..e7d8782 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroid.java
@@ -4,74 +4,38 @@
 
 package org.chromium.chrome.browser.dragdrop;
 
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.ui.dragdrop.DropDataAndroid;
 
-/** */
-public class ChromeDropDataAndroid extends DropDataAndroid {
-    public final Tab tab;
-    public final boolean isTabInGroup;
-    public final boolean allowTabDragToCreateInstance;
+/** Chrome-specific drop data. */
+public abstract class ChromeDropDataAndroid extends DropDataAndroid {
+    public final boolean allowDragToCreateInstance;
     public final int windowId;
 
     /** Not generated from java */
     ChromeDropDataAndroid(Builder builder) {
         super(null, null, null, null, null);
-        tab = builder.mTab;
-        isTabInGroup = builder.mIsTabInGroup;
-        allowTabDragToCreateInstance = builder.mAllowTabDragToCreateInstance;
+        allowDragToCreateInstance = builder.mAllowDragToCreateInstance;
         windowId = builder.mWindowId;
     }
 
-    public boolean hasTab() {
-        return tab != null;
-    }
-
-    @Override
-    public boolean hasBrowserContent() {
-        return hasTab();
-    }
+    /** Returns true if the associated browser content is Incognito. */
+    public abstract boolean isIncognito();
 
     /** Build clip data text with tab info. */
-    public String buildTabClipDataText() {
-        if (hasTab()) {
-            return tab.getUrl().getSpec();
-        }
-        return null;
-    }
+    public abstract String buildTabClipDataText();
 
     /** Builder for @{@link ChromeDropDataAndroid} instance. */
-    public static class Builder {
-        private Tab mTab;
-        private boolean mIsTabInGroup;
-        private boolean mAllowTabDragToCreateInstance;
+    public abstract static class Builder {
+        private boolean mAllowDragToCreateInstance;
         private int mWindowId;
 
         /**
-         * @param tab to be set in clip data.
-         * @return {@link ChromeDropDataAndroid.Builder} instance.
-         */
-        public Builder withTab(Tab tab) {
-            mTab = tab;
-            return this;
-        }
-
-        /**
-         * @param isTabInGroup Whether the dragged tab is in a tab group.
-         * @return {@link ChromeDropDataAndroid.Builder} instance.
-         */
-        public Builder withTabInGroup(boolean isTabInGroup) {
-            mIsTabInGroup = isTabInGroup;
-            return this;
-        }
-
-        /**
          * @param allowDragToCreateInstance Whether tab drag to create new instance should be
          *     allowed.
          * @return {@link ChromeDropDataAndroid.Builder} instance.
          */
         public Builder withAllowDragToCreateInstance(boolean allowDragToCreateInstance) {
-            mAllowTabDragToCreateInstance = allowDragToCreateInstance;
+            mAllowDragToCreateInstance = allowDragToCreateInstance;
             return this;
         }
 
@@ -87,8 +51,6 @@
         /**
          * @return new @{@link ChromeDropDataAndroid} instance.
          */
-        public ChromeDropDataAndroid build() {
-            return new ChromeDropDataAndroid(this);
-        }
+        public abstract ChromeDropDataAndroid build();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabDropDataAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabDropDataAndroid.java
new file mode 100644
index 0000000..fa80490
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabDropDataAndroid.java
@@ -0,0 +1,63 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.dragdrop;
+
+import org.chromium.chrome.browser.tab.Tab;
+
+/** Chrome-specific drop data containing a {@link Tab}. */
+public class ChromeTabDropDataAndroid extends ChromeDropDataAndroid {
+    public final Tab tab;
+    public final boolean isTabInGroup;
+
+    ChromeTabDropDataAndroid(Builder builder) {
+        super(builder);
+        tab = builder.mTab;
+        isTabInGroup = builder.mIsTabInGroup;
+    }
+
+    @Override
+    public boolean hasBrowserContent() {
+        return tab != null;
+    }
+
+    @Override
+    public boolean isIncognito() {
+        return hasBrowserContent() && tab.isIncognitoBranded();
+    }
+
+    @Override
+    public String buildTabClipDataText() {
+        return hasBrowserContent() ? tab.getUrl().getSpec() : null;
+    }
+
+    /** Builder for @{@link ChromeTabDropDataAndroid} instance. */
+    public static class Builder extends ChromeDropDataAndroid.Builder {
+        private Tab mTab;
+        private boolean mIsTabInGroup;
+
+        /**
+         * @param tab to be set in clip data.
+         * @return {@link ChromeTabDropDataAndroid.Builder} instance.
+         */
+        public Builder withTab(Tab tab) {
+            mTab = tab;
+            return this;
+        }
+
+        /**
+         * @param isTabInGroup Whether the dragged tab is in a tab group.
+         * @return {@link ChromeTabDropDataAndroid.Builder} instance.
+         */
+        public Builder withTabInGroup(boolean isTabInGroup) {
+            mIsTabInGroup = isTabInGroup;
+            return this;
+        }
+
+        @Override
+        public ChromeDropDataAndroid build() {
+            return new ChromeTabDropDataAndroid(/* builder= */ this);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabGroupDropDataAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabGroupDropDataAndroid.java
new file mode 100644
index 0000000..af7d02f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabGroupDropDataAndroid.java
@@ -0,0 +1,52 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.dragdrop;
+
+import org.chromium.chrome.browser.tabmodel.TabGroupMetadata;
+
+/** Chrome-specific drop data containing a {@link TabGroupMetadata}. */
+public class ChromeTabGroupDropDataAndroid extends ChromeDropDataAndroid {
+    public final TabGroupMetadata tabGroupMetadata;
+
+    ChromeTabGroupDropDataAndroid(Builder builder) {
+        super(builder);
+        tabGroupMetadata = builder.mTabGroupMetadata;
+    }
+
+    @Override
+    public boolean hasBrowserContent() {
+        return tabGroupMetadata != null;
+    }
+
+    @Override
+    public boolean isIncognito() {
+        return hasBrowserContent() && tabGroupMetadata.isIncognito;
+    }
+
+    @Override
+    public String buildTabClipDataText() {
+        // TODO(crbug.com/380327012): Implement clip data text for groups.
+        return null;
+    }
+
+    /** Builder for @{@link ChromeTabDropDataAndroid} instance. */
+    public static class Builder extends ChromeDropDataAndroid.Builder {
+        private TabGroupMetadata mTabGroupMetadata;
+
+        /**
+         * @param tabGroupMetadata The {@link TabGroupMetadata} associated with the dragging group.
+         * @return {@link ChromeTabGroupDropDataAndroid.Builder} instance.
+         */
+        public Builder withTabGroupMetadata(TabGroupMetadata tabGroupMetadata) {
+            mTabGroupMetadata = tabGroupMetadata;
+            return this;
+        }
+
+        @Override
+        public ChromeDropDataAndroid build() {
+            return new ChromeTabGroupDropDataAndroid(/* builder= */ this);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java
index 004a003b..815738e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListener.java
@@ -129,8 +129,8 @@
     private Tab getTabFromGlobalState(@NonNull DragDropGlobalState globalState) {
         // We should only attempt to access this while we know there's an active drag.
         assert globalState != null : "Attempting to access dragged tab with invalid drag state.";
-        if (globalState.getData() instanceof ChromeDropDataAndroid) {
-            return ((ChromeDropDataAndroid) globalState.getData()).tab;
+        if (globalState.getData() instanceof ChromeTabDropDataAndroid) {
+            return ((ChromeTabDropDataAndroid) globalState.getData()).tab;
         } else {
             return null;
         }
@@ -139,8 +139,8 @@
     private boolean isTabInGroupFromGlobalState(@NonNull DragDropGlobalState globalState) {
         // We should only attempt to access this while we know there's an active drag.
         assert globalState != null : "Attempting to access dragged tab with invalid drag state.";
-        if (globalState.getData() instanceof ChromeDropDataAndroid) {
-            return ((ChromeDropDataAndroid) globalState.getData()).isTabInGroup;
+        if (globalState.getData() instanceof ChromeTabDropDataAndroid) {
+            return ((ChromeTabDropDataAndroid) globalState.getData()).isTabInGroup;
         } else {
             return false;
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java
index 44a726f7..13cca22 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabDragSourceTest.java
@@ -70,6 +70,7 @@
 import org.chromium.chrome.browser.compositor.overlays.strip.TestTabModel;
 import org.chromium.chrome.browser.compositor.overlays.strip.reorder.TabDragSource.TabDragShadowBuilder;
 import org.chromium.chrome.browser.dragdrop.ChromeDropDataAndroid;
+import org.chromium.chrome.browser.dragdrop.ChromeTabDropDataAndroid;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
 import org.chromium.chrome.browser.multiwindow.MultiWindowTestUtils;
@@ -259,7 +260,7 @@
         assertEquals(
                 "Global state tabBeingDragged not set.",
                 mTabBeingDragged,
-                ((ChromeDropDataAndroid) DragDropGlobalState.getForTesting().getData()).tab);
+                ((ChromeTabDropDataAndroid) DragDropGlobalState.getForTesting().getData()).tab);
         assertNull("Shadow view should be null.", mSourceInstance.getShadowViewForTesting());
     }
 
@@ -286,7 +287,7 @@
         assertEquals(
                 "Global state tabBeingDragged not set.",
                 mTabBeingDragged,
-                ((ChromeDropDataAndroid) DragDropGlobalState.getForTesting().getData()).tab);
+                ((ChromeTabDropDataAndroid) DragDropGlobalState.getForTesting().getData()).tab);
         assertNotNull(
                 "Shadow view is unexpectedly null.", mSourceInstance.getShadowViewForTesting());
     }
@@ -377,7 +378,7 @@
         when(mTabModelSelector.getTotalTabCount()).thenReturn(2);
 
         // Verify.
-        callAndVerifyAllowTabDragToCreateInstance(true);
+        callAndVerifyAllowDragToCreateInstance(true);
     }
 
     @Test
@@ -408,7 +409,7 @@
         MultiWindowUtils.setMaxInstancesForTesting(5);
 
         // Verify.
-        callAndVerifyAllowTabDragToCreateInstance(false);
+        callAndVerifyAllowDragToCreateInstance(false);
     }
 
     @Test
@@ -421,7 +422,7 @@
         MultiWindowUtils.setMaxInstancesForTesting(5);
         ReflectionHelpers.setStaticField(Build.class, "MANUFACTURER", "samsung");
 
-        callAndVerifyAllowTabDragToCreateInstance(true);
+        callAndVerifyAllowDragToCreateInstance(true);
     }
 
     @Test
@@ -433,7 +434,7 @@
         MultiWindowUtils.setMaxInstancesForTesting(5);
 
         // Verify.
-        callAndVerifyAllowTabDragToCreateInstance(false);
+        callAndVerifyAllowDragToCreateInstance(false);
     }
 
     @Test
@@ -1285,7 +1286,8 @@
     }
 
     private DragEvent mockDragEvent(int action, float x, float y, Tab tab) {
-        ChromeDropDataAndroid dropData = new ChromeDropDataAndroid.Builder().withTab(tab).build();
+        ChromeDropDataAndroid dropData =
+                new ChromeTabDropDataAndroid.Builder().withTab(tab).build();
         DragEvent event = mock(DragEvent.class);
         when(event.getAction()).thenReturn(action);
         when(event.getX()).thenReturn(x);
@@ -1300,8 +1302,7 @@
         return event;
     }
 
-    private void callAndVerifyAllowTabDragToCreateInstance(
-            boolean expectedAllowTabDragToCreateInstance) {
+    private void callAndVerifyAllowDragToCreateInstance(boolean expectedAllowDragToCreateInstance) {
         // Verify.
         assertTrue(
                 "Tab drag should start.",
@@ -1318,8 +1319,8 @@
                         any(DragShadowBuilder.class),
                         dropDataCaptor.capture());
         assertEquals(
-                "DropData.allowTabDragToCreateInstance value is not as expected.",
-                expectedAllowTabDragToCreateInstance,
-                dropDataCaptor.getValue().allowTabDragToCreateInstance);
+                "DropData.allowDragToCreateInstance value is not as expected.",
+                expectedAllowDragToCreateInstance,
+                dropDataCaptor.getValue().allowDragToCreateInstance);
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegateUnitTest.java
index 5393ce2..88ec7923b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDragAndDropBrowserDelegateUnitTest.java
@@ -207,7 +207,7 @@
     public void testBuildFlags_dropDataHasNoTab() {
         MultiWindowTestUtils.enableMultiInstance();
         int originalFlag = 0;
-        var dropData = new ChromeDropDataAndroid.Builder().build();
+        var dropData = new ChromeTabDropDataAndroid.Builder().build();
         var flags = mDelegate.buildFlags(originalFlag, dropData);
         assertEquals("Original flag should not be modified.", originalFlag, flags);
     }
@@ -238,7 +238,7 @@
     private ChromeDropDataAndroid createTabDropData(
             int tabId, boolean allowDragToCreateNewInstance) {
         Tab tab = MockTab.createAndInitialize(tabId, mProfile);
-        return new ChromeDropDataAndroid.Builder()
+        return new ChromeTabDropDataAndroid.Builder()
                 .withTab(tab)
                 .withAllowDragToCreateInstance(allowDragToCreateNewInstance)
                 .build();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroidTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroidTest.java
index 7455093..7b6d076 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroidTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeDropDataAndroidTest.java
@@ -29,7 +29,7 @@
     public void testBuildTabClipDataText() {
         when(mTab.getId()).thenReturn(1);
         when(mTab.getUrl()).thenReturn(JUnitTestGURLs.EXAMPLE_URL);
-        ChromeDropDataAndroid data = new ChromeDropDataAndroid.Builder().withTab(mTab).build();
+        ChromeDropDataAndroid data = new ChromeTabDropDataAndroid.Builder().withTab(mTab).build();
         assertEquals(
                 "Clip data text is not as expected.",
                 JUnitTestGURLs.EXAMPLE_URL.getSpec(),
@@ -38,7 +38,7 @@
 
     @Test
     public void testBuildTabClipDataTextWithNullTab() {
-        ChromeDropDataAndroid data = new ChromeDropDataAndroid.Builder().build();
+        ChromeDropDataAndroid data = new ChromeTabDropDataAndroid.Builder().build();
         assertNull("Clip data text is not as expected.", data.buildTabClipDataText());
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
index 12e1f05af..1a83dd6 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dragdrop/ChromeTabbedOnDragListenerUnitTest.java
@@ -87,7 +87,7 @@
         when(mTabModelSelector.getModel(false)).thenReturn(Mockito.mock(TabModel.class));
         when(mMultiInstanceManager.getCurrentInstanceId()).thenReturn(SOURCE_INSTANCE_ID);
         when(mDragDropGlobalState.getData())
-                .thenReturn(new ChromeDropDataAndroid.Builder().withTab(mTab).build());
+                .thenReturn(new ChromeTabDropDataAndroid.Builder().withTab(mTab).build());
         when(mDragDropGlobalState.isDragSourceInstance(SOURCE_INSTANCE_ID)).thenReturn(true);
         DragDropGlobalState.setInstanceForTesting(mDragDropGlobalState);
         Activity activity = Mockito.mock(Activity.class);
@@ -213,7 +213,7 @@
         // The tab being dragged is in a tab group.
         when(mDragDropGlobalState.getData())
                 .thenReturn(
-                        new ChromeDropDataAndroid.Builder()
+                        new ChromeTabDropDataAndroid.Builder()
                                 .withTab(mTab)
                                 .withTabInGroup(true)
                                 .build());