Commit 1ca0b7a3 authored by Collin Baker's avatar Collin Baker Committed by Commit Bot

Drop Mohnstrudel tabs into new window when possible

Ash's DragDropController is modified to specially handle drags from the
WebUI tab strip when a flag is enabled. Dropping a tab over an area that
doesn't support accepting the drop will open a new window with only that
tab.

Bug: 1069869
Change-Id: I9ee06a497f8557a3c8e0defff10c8edc1993514c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2130812Reviewed-by: default avatarJohn Lee <johntlee@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Commit-Queue: Collin Baker <collinbaker@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758922}
parent c068b079
......@@ -9,10 +9,14 @@
#include "ash/drag_drop/drag_drop_tracker.h"
#include "ash/drag_drop/drag_image_view.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/pickle.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/aura/client/capture_client.h"
......@@ -22,6 +26,8 @@
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/hit_test.h"
......@@ -466,6 +472,9 @@ void DragDropController::DragUpdate(aura::Window* target,
cursor = ui::mojom::CursorType::kAlias;
else if (op & ui::DragDropTypes::DRAG_MOVE)
cursor = ui::mojom::CursorType::kGrabbing;
// TODO(https://crbug.com/1069869): don't show kNoDrop cursor for
// a tab drag that can drop into a new window.
Shell::Get()->cursor_manager()->SetCursor(cursor);
}
}
......@@ -503,15 +512,24 @@ void DragDropController::Drop(aura::Window* target,
aura::client::DragDropDelegate* delegate =
aura::client::GetDragDropDelegate(target);
if (delegate) {
const bool is_chrome_tab_drag = IsChromeTabDrag();
ui::DropTargetEvent e(*drag_data_.get(), event.location_f(),
event.root_location_f(), drag_operation_);
e.set_flags(event.flags());
ui::Event::DispatcherApi(&e).set_target(target);
ui::OSExchangeData copied_data(drag_data_->provider().Clone());
drag_operation_ = delegate->OnPerformDrop(e, std::move(drag_data_));
if (drag_operation_ == 0)
if (drag_operation_ == 0 && is_chrome_tab_drag) {
Shell::Get()->shell_delegate()->CreateBrowserForTabDrop(
drag_source_window_, copied_data);
StartCanceledAnimation(kCancelAnimationDuration);
} else if (drag_operation_ == 0) {
StartCanceledAnimation(kCancelAnimationDuration);
else
} else {
drag_image_.reset();
}
} else {
drag_image_.reset();
}
......@@ -620,4 +638,37 @@ void DragDropController::Cleanup() {
std::unique_ptr<DragDropTracker> holder = std::move(drag_drop_tracker_);
}
bool DragDropController::IsChromeTabDrag() {
if (!features::IsWebUITabStripTabDragIntegrationEnabled())
return false;
if (!drag_data_)
return false;
base::Pickle pickle;
drag_data_->GetPickledData(ui::ClipboardFormatType::GetWebCustomDataType(),
&pickle);
base::PickleIterator iter(pickle);
uint32_t entry_count = 0;
if (!iter.ReadUInt32(&entry_count))
return false;
for (uint32_t i = 0; i < entry_count; ++i) {
base::StringPiece16 type;
base::StringPiece16 data;
if (!iter.ReadStringPiece16(&type) || !iter.ReadStringPiece16(&data)) {
return false;
}
// TODO(https://crbug.com/1069869): share this constant between Ash
// and Chrome instead of hardcoding it in both places.
static const base::string16 chrome_tab_type =
base::ASCIIToUTF16("application/vnd.chromium.tab");
if (type == chrome_tab_type)
return true;
}
return false;
}
} // namespace ash
......@@ -111,6 +111,8 @@ class ASH_EXPORT DragDropController : public aura::client::DragDropClient,
// Helper method to reset everything.
void Cleanup();
bool IsChromeTabDrag();
bool enabled_ = false;
std::unique_ptr<DragImageView> drag_image_;
gfx::Vector2d drag_image_offset_;
......
......@@ -6,6 +6,7 @@
#include "ash/public/cpp/ash_switches.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "build/build_config.h"
#include "chromeos/constants/chromeos_switches.h"
......@@ -120,6 +121,9 @@ const base::Feature kHideShelfControlsInTabletMode{
const base::Feature kSystemTrayMicGainSetting{
"SystemTrayMicGainSetting", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kWebUITabStripTabDragIntegration{
"WebUITabStripTabDragIntegration", base::FEATURE_DISABLED_BY_DEFAULT};
bool IsAllowAmbientEQEnabled() {
return base::FeatureList::IsEnabled(kAllowAmbientEQ);
}
......@@ -248,6 +252,10 @@ bool IsDisplayIdentificationEnabled() {
return base::FeatureList::IsEnabled(kDisplayIdentification);
}
bool IsWebUITabStripTabDragIntegrationEnabled() {
return base::FeatureList::IsEnabled(kWebUITabStripTabDragIntegration);
}
namespace {
// The boolean flag indicating if "WebUITabStrip" feature is enabled in Chrome.
......
......@@ -154,6 +154,12 @@ ASH_PUBLIC_EXPORT extern const base::Feature kHideShelfControlsInTabletMode;
// section in the system tray.
ASH_PUBLIC_EXPORT extern const base::Feature kSystemTrayMicGainSetting;
// Enables special handling of Chrome tab drags from a WebUI tab strip.
// These will be treated similarly to a window drag, showing split view
// indicators in tablet mode, etc. The functionality is behind a flag
// right now since it is under development.
ASH_PUBLIC_EXPORT extern const base::Feature kWebUITabStripTabDragIntegration;
ASH_PUBLIC_EXPORT bool IsAllowAmbientEQEnabled();
ASH_PUBLIC_EXPORT bool IsAltTabLimitedToActiveDesk();
......@@ -210,6 +216,8 @@ ASH_PUBLIC_EXPORT bool IsSystemTrayMicGainSettingEnabled();
ASH_PUBLIC_EXPORT bool IsDisplayIdentificationEnabled();
ASH_PUBLIC_EXPORT bool IsWebUITabStripTabDragIntegrationEnabled();
// These two functions are supposed to be temporary functions to set or get
// whether "WebUITabStrip" feature is enabled from Chrome.
ASH_PUBLIC_EXPORT void SetWebUITabStripEnabled(bool enabled);
......
......@@ -6,6 +6,12 @@
namespace ash {
bool ShellDelegate::CreateBrowserForTabDrop(
gfx::NativeWindow source_window,
const ui::OSExchangeData& drop_data) {
return false;
}
media_session::mojom::MediaSessionService*
ShellDelegate::GetMediaSessionService() {
return nullptr;
......
......@@ -23,6 +23,10 @@ namespace aura {
class Window;
}
namespace ui {
class OSExchangeData;
}
namespace ash {
class AccessibilityDelegate;
......@@ -54,6 +58,9 @@ class ASH_EXPORT ShellDelegate {
// Check whether the current tab of the browser window can go back.
virtual bool CanGoBack(gfx::NativeWindow window) const = 0;
virtual bool CreateBrowserForTabDrop(gfx::NativeWindow source_window,
const ui::OSExchangeData& drop_data);
// Binds a BluetoothSystemFactory receiver if possible.
virtual void BindBluetoothSystemFactory(
mojo::PendingReceiver<device::mojom::BluetoothSystemFactory> receiver) {}
......
......@@ -6,6 +6,7 @@
#include <memory>
#include "ash/public/cpp/ash_features.h"
#include "ash/screenshot_delegate.h"
#include "base/bind.h"
#include "chrome/browser/browser_process.h"
......@@ -26,6 +27,7 @@
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_util.h"
#include "chromeos/services/multidevice_setup/multidevice_setup_service.h"
#include "content/public/browser/device_service.h"
#include "content/public/browser/media_session_service.h"
......@@ -71,6 +73,36 @@ bool ChromeShellDelegate::CanGoBack(gfx::NativeWindow window) const {
return contents->GetController().CanGoBack();
}
bool ChromeShellDelegate::CreateBrowserForTabDrop(
gfx::NativeWindow source_window,
const ui::OSExchangeData& drop_data) {
CHECK(ash::features::IsWebUITabStripTabDragIntegrationEnabled());
BrowserView* source_view = BrowserView::GetBrowserViewForNativeWindow(
source_window->GetToplevelWindow());
if (!source_view)
return false;
Browser::CreateParams params = source_view->browser()->create_params();
params.user_gesture = true;
params.initial_show_state = ui::SHOW_STATE_DEFAULT;
Browser* browser = Browser::Create(params);
if (!browser)
return false;
if (!tab_strip_ui::DropTabsInNewBrowser(browser, drop_data)) {
browser->window()->Close();
return false;
}
// TODO(https://crbug.com/1069869): evaluate whether the above
// failures can happen in valid states, and if so whether we need to
// reflect failure in UX.
browser->window()->Show();
return true;
}
void ChromeShellDelegate::BindBluetoothSystemFactory(
mojo::PendingReceiver<device::mojom::BluetoothSystemFactory> receiver) {
content::GetDeviceService().BindBluetoothSystemFactory(std::move(receiver));
......
......@@ -22,6 +22,8 @@ class ChromeShellDelegate : public ash::ShellDelegate {
ash::BackGestureContextualNudgeController* controller) override;
void OpenKeyboardShortcutHelpPage() const override;
bool CanGoBack(gfx::NativeWindow window) const override;
bool CreateBrowserForTabDrop(gfx::NativeWindow source_window,
const ui::OSExchangeData& drop_data) override;
void BindBluetoothSystemFactory(
mojo::PendingReceiver<device::mojom::BluetoothSystemFactory> receiver)
override;
......
......@@ -4,12 +4,19 @@
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui.h"
#include "components/tab_groups/tab_group_id.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/dragdrop/os_exchange_data.h"
namespace tab_strip_ui {
......@@ -65,4 +72,37 @@ void MoveTabAcrossWindows(Browser* source_browser,
to_index, std::move(detached_contents), add_types, to_group_id);
}
bool DropTabsInNewBrowser(Browser* new_browser,
const ui::OSExchangeData& drop_data) {
base::Pickle pickle;
drop_data.GetPickledData(ui::ClipboardFormatType::GetWebCustomDataType(),
&pickle);
base::string16 tab_id_str;
ui::ReadCustomDataForType(pickle.data(), pickle.size(),
base::ASCIIToUTF16(kWebUITabIdDataType),
&tab_id_str);
if (tab_id_str.empty())
return false;
// |tab_id_str| should contain the extension tab id as a string.
int tab_id = -1;
if (!base::StringToInt(tab_id_str, &tab_id))
return false;
Browser* source_browser = nullptr;
int source_index = -1;
if (!extensions::ExtensionTabUtil::GetTabById(
tab_id, new_browser->profile(), /* include_incognito = */ false,
&source_browser, /* tab_strip = */ nullptr,
/* contents = */ nullptr, &source_index)) {
return false;
}
MoveTabAcrossWindows(source_browser, source_index, new_browser, 0,
base::nullopt);
new_browser->tab_strip_model()->ActivateTabAt(0);
return true;
}
} // namespace tab_strip_ui
......@@ -12,6 +12,10 @@ class Browser;
class Profile;
class TabGroupModel;
namespace ui {
class OSExchangeData;
}
namespace tab_strip_ui {
base::Optional<tab_groups::TabGroupId> GetTabGroupIdFromString(
......@@ -27,6 +31,15 @@ void MoveTabAcrossWindows(
int to_index,
base::Optional<tab_groups::TabGroupId> to_group_id = base::nullopt);
// Handles dropping tabs not destined for an existing tab strip.
// |new_browser| should be the newly created Browser with no tabs, and
// must have the same profile as the drag source. |drop_data| must have
// originated from a drag in a WebUI tab strip. If successful, the tabs
// reflected in |drop_data| will be moved from the source browser to
// |new_browser|.
bool DropTabsInNewBrowser(Browser* new_browser,
const ui::OSExchangeData& drop_data);
} // namespace tab_strip_ui
#endif // CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_UI_UTIL_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment