Commit 93f94cdf authored by Xiaoqian Dai's avatar Xiaoqian Dai Committed by Commit Bot

Fix the crash when fling a tab out of a window.

Bug: 895649
Change-Id: I4f4f94639ef7d4a68b37163451afc5eb1dcc77ec
Reviewed-on: https://chromium-review.googlesource.com/c/1285086
Commit-Queue: Xiaoqian Dai <xdai@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600554}
parent a614021b
......@@ -1708,6 +1708,22 @@ class SplitViewTabDraggingTest : public SplitViewControllerTest {
SetIsInTabDragging(resizer->GetTarget(), /*is_dragging=*/false);
}
// Fling to end the drag. |resizer| will be deleted after exiting this
// function.
void Fling(std::unique_ptr<WindowResizer> resizer, float velocity_y) {
ASSERT_TRUE(resizer.get());
aura::Window* target_window = resizer->GetTarget();
base::TimeTicks timestamp = base::TimeTicks::Now();
ui::GestureEventDetails details =
ui::GestureEventDetails(ui::ET_SCROLL_FLING_START, 0.f, velocity_y);
ui::GestureEvent event = ui::GestureEvent(
target_window->bounds().origin().x(),
target_window->bounds().origin().y(), ui::EF_NONE, timestamp, details);
ui::Event::DispatcherApi(&event).set_target(target_window);
resizer->FlingOrSwipe(&event);
SetIsInTabDragging(resizer->GetTarget(), /*is_dragging=*/false);
}
std::unique_ptr<WindowResizer> CreateResizerForTest(
aura::Window* window,
const gfx::Point& point_in_parent,
......@@ -3093,6 +3109,29 @@ TEST_F(SplitViewTabDraggingTest, DragWindowIntoPreviewAreaAndDropTarget) {
split_view_controller()->state());
}
// Tests that if a fling event happens on a tab, the tab might or might not
// merge back into the source window depending on the fling event velocity.
TEST_F(SplitViewTabDraggingTest, FlingTest) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> dragged_window(
CreateWindowWithType(bounds, AppType::BROWSER));
std::unique_ptr<aura::Window> source_window(
CreateWindowWithType(bounds, AppType::BROWSER));
std::unique_ptr<WindowResizer> resizer =
StartDrag(dragged_window.get(), source_window.get());
ASSERT_TRUE(resizer.get());
Fling(std::move(resizer), /*velocity_y=*/3000.f);
EXPECT_FALSE(
source_window->GetProperty(ash::kIsDeferredTabDraggingTargetWindowKey));
resizer = StartDrag(dragged_window.get(), source_window.get());
ASSERT_TRUE(resizer.get());
Fling(std::move(resizer), /*velocity_y=*/1000.f);
EXPECT_TRUE(
source_window->GetProperty(ash::kIsDeferredTabDraggingTargetWindowKey));
}
class TestWindowDelegateWithWidget : public views::WidgetDelegate {
public:
TestWindowDelegateWithWidget(bool can_activate)
......
......@@ -5,6 +5,7 @@
#include "ash/wm/tablet_mode/tablet_mode_app_window_drag_controller.h"
#include "ash/shell.h"
#include "ash/wm/overview/window_selector_controller.h"
#include "ash/wm/splitview/split_view_drag_indicators.h"
#include "ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h"
#include "ash/wm/window_state.h"
......@@ -36,6 +37,13 @@ class TabletModeAppWindowDragDelegate : public TabletModeWindowDragDelegate {
wm::GetWindowState(dragged_window_)->DeleteDragDetails();
}
void EndedWindowDrag(const gfx::Point& location_in_screen) override {}
void StartFling(const ui::GestureEvent* event) override {
if (ShouldFlingIntoOverview(event)) {
DCHECK(Shell::Get()->window_selector_controller()->IsSelecting());
Shell::Get()->window_selector_controller()->window_selector()->AddItem(
dragged_window_, /*reposition=*/true, /*animate=*/false);
}
}
DISALLOW_COPY_AND_ASSIGN(TabletModeAppWindowDragDelegate);
};
......
......@@ -36,6 +36,10 @@ namespace {
// threshold.
constexpr float kSourceWindowScale = 0.85;
// Threshold of the fling velocity to keep the dragged window as a new separate
// window after drag ends and do not try to merge it back into source window.
constexpr float kFlingToStayAsNewWindowThreshold = 2000.f;
// The class to observe the source window's bounds change animation. It's used
// to prevent the dragged window to merge back into the source window during
// dragging. Only when the source window restores to its maximized window size,
......@@ -281,6 +285,22 @@ void TabletModeBrowserWindowDragDelegate::EndedWindowDrag(
MergeBackToSourceWindowIfApplicable(location_in_screen);
}
void TabletModeBrowserWindowDragDelegate::StartFling(
const ui::GestureEvent* event) {
if (ShouldFlingIntoOverview(event)) {
DCHECK(Shell::Get()->window_selector_controller()->IsSelecting());
Shell::Get()->window_selector_controller()->window_selector()->AddItem(
dragged_window_, /*reposition=*/true, /*animate=*/false);
} else {
aura::Window* source_window =
dragged_window_->GetProperty(ash::kTabDraggingSourceWindowKey);
if (source_window &&
event->details().velocity_y() > kFlingToStayAsNewWindowThreshold) {
can_merge_back_to_source_window_ = false;
}
}
}
bool TabletModeBrowserWindowDragDelegate::ShouldOpenOverviewWhenDragStarts() {
DCHECK(dragged_window_);
aura::Window* source_window =
......@@ -361,6 +381,12 @@ void TabletModeBrowserWindowDragDelegate::MergeBackToSourceWindowIfApplicable(
return;
}
// Do not merge back if the dragged window is not capable of merging back.
// This may happen if the drag ends because of a fling event and the fling
// velocity has exceeded kFlingToStayAsNewWindowThreshold.
if (!can_merge_back_to_source_window_)
return;
// Do not merge back if the window has dragged farther than half of the screen
// height.
const gfx::Rect work_area_bounds =
......
......@@ -29,6 +29,7 @@ class TabletModeBrowserWindowDragDelegate
void EndingWindowDrag(wm::WmToplevelWindowEventHandler::DragResult result,
const gfx::Point& location_in_screen) override;
void EndedWindowDrag(const gfx::Point& location_in_screen) override;
void StartFling(const ui::GestureEvent* event) override;
bool ShouldOpenOverviewWhenDragStarts() override;
// Scales down the source window if the dragged window is dragged past the
......@@ -59,6 +60,11 @@ class TabletModeBrowserWindowDragDelegate
// source window.
std::unique_ptr<ui::ImplicitAnimationObserver> source_window_bounds_observer_;
// True if the dragged window is capable of merging back to source window
// after drag ends. If it's false, it means the drag ends because of a fling
// event and the fling velocity has exceeded kFlingToStayAsNewWindowThreshold.
bool can_merge_back_to_source_window_ = true;
DISALLOW_COPY_AND_ASSIGN(TabletModeBrowserWindowDragDelegate);
};
......
......@@ -204,11 +204,7 @@ void TabletModeWindowDragDelegate::EndWindowDrag(
}
void TabletModeWindowDragDelegate::FlingOrSwipe(ui::GestureEvent* event) {
if (ShouldFlingIntoOverview(event)) {
DCHECK(Shell::Get()->window_selector_controller()->IsSelecting());
Shell::Get()->window_selector_controller()->window_selector()->AddItem(
dragged_window_, /*reposition=*/true, /*animate=*/false);
}
StartFling(event);
EndWindowDrag(wm::WmToplevelWindowEventHandler::DragResult::SUCCESS,
GetEventLocationInScreen(event));
}
......@@ -395,6 +391,13 @@ bool TabletModeWindowDragDelegate::ShouldFlingIntoOverview(
if (event->type() != ui::ET_SCROLL_FLING_START)
return false;
// Only fling into overview if overview is currently open. In some case,
// overview is not opened when drag starts (if it's tab-dragging and the
// dragged window is not the same with the source window), we should not fling
// the dragged window into overview in this case.
if (!Shell::Get()->window_selector_controller()->IsSelecting())
return false;
const gfx::Point location_in_screen = GetEventLocationInScreen(event);
const IndicatorState indicator_state = GetIndicatorState(location_in_screen);
const bool is_landscape =
......
......@@ -86,6 +86,8 @@ class TabletModeWindowDragDelegate {
wm::WmToplevelWindowEventHandler::DragResult result,
const gfx::Point& location_in_screen) = 0;
virtual void EndedWindowDrag(const gfx::Point& location_in_screen) = 0;
// Calls when a fling event starts.
virtual void StartFling(const ui::GestureEvent* event) = 0;
// Returns true if we should open overview behind the dragged window when drag
// starts.
......
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