diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index ce7acd9c04fe2..53603a60f5fc8 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1366,8 +1366,8 @@ component("ash") {
     "wm/tablet_mode/scoped_skip_user_session_blocked_check.h",
     "wm/tablet_mode/tablet_mode_browser_window_drag_delegate.cc",
     "wm/tablet_mode/tablet_mode_controller.cc",
-    "wm/tablet_mode/tablet_mode_event_handler.cc",
-    "wm/tablet_mode/tablet_mode_event_handler.h",
+    "wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.cc",
+    "wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h",
     "wm/tablet_mode/tablet_mode_window_drag_delegate.cc",
     "wm/tablet_mode/tablet_mode_window_drag_metrics.cc",
     "wm/tablet_mode/tablet_mode_window_drag_metrics.h",
@@ -2094,6 +2094,7 @@ test("ash_unittests") {
     "wm/system_modal_container_layout_manager_unittest.cc",
     "wm/tablet_mode/accelerometer_test_data_literals.cc",
     "wm/tablet_mode/tablet_mode_controller_unittest.cc",
+    "wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc",
     "wm/tablet_mode/tablet_mode_window_manager_unittest.cc",
     "wm/toplevel_window_event_handler_unittest.cc",
     "wm/video_detector_unittest.cc",
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index 111e186ecc1b0..c5e9f075edb84 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -116,6 +116,10 @@ class ASH_EXPORT ShelfConfig : public TabletModeObserver,
   // Returns whether we are within an app.
   bool is_in_app() const;
 
+  // The threshold relative to the size of the shelf that is used to determine
+  // if the shelf visibility should change during a drag.
+  float drag_hide_ratio_threshold() const;
+
   int app_icon_group_margin() const { return app_icon_group_margin_; }
   SkColor shelf_control_permanent_highlight_background() const {
     return shelf_control_permanent_highlight_background_;
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index 2ff3680cdf197..a9e14c0b6f28f 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -36,7 +36,11 @@ constexpr int kControlButtonsShownReasonCount = 1 << 4;
 
 // When any edge of the primary display is less than or equal to this threshold,
 // dense shelf will be active.
-const int kDenseShelfScreenSizeThreshold = 600;
+constexpr int kDenseShelfScreenSizeThreshold = 600;
+
+// Drags on the shelf that are greater than this number times the shelf size
+// will trigger shelf visibility changes.
+constexpr float kDragHideRatioThreshold = 0.4f;
 
 // Records the histogram value tracking the reason shelf control buttons are
 // shown in tablet mode.
@@ -305,6 +309,10 @@ bool ShelfConfig::is_in_app() const {
          (!is_app_list_visible_ || is_virtual_keyboard_shown_);
 }
 
+float ShelfConfig::drag_hide_ratio_threshold() const {
+  return kDragHideRatioThreshold;
+}
+
 void ShelfConfig::UpdateConfig(bool app_list_visible) {
   const gfx::Rect screen_size =
       display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index b3a1ff9d0366c..ea4db255b21f5 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -2508,13 +2508,13 @@ bool ShelfLayoutManager::ShouldChangeVisibilityAfterDrag(
     // The visibility of the shelf changes only if the shelf was dragged X%
     // along the correct axis. If the shelf was already visible, then the
     // direction of the drag does not matter.
-    const float kDragHideThreshold = 0.4f;
     const gfx::Rect bounds = GetIdealBounds();
     const float drag_ratio =
         fabs(drag_amount_) /
         (shelf_->IsHorizontalAlignment() ? bounds.height() : bounds.width());
 
-    return IsSwipingCorrectDirection() && drag_ratio > kDragHideThreshold;
+    return IsSwipingCorrectDirection() &&
+           drag_ratio > ShelfConfig::Get()->drag_hide_ratio_threshold();
   }
 
   if (event_in_screen.type() == ui::ET_SCROLL_FLING_START)
diff --git a/ash/wm/tablet_mode/tablet_mode_event_handler.cc b/ash/wm/tablet_mode/tablet_mode_event_handler.cc
deleted file mode 100644
index 46d51856debbd..0000000000000
--- a/ash/wm/tablet_mode/tablet_mode_event_handler.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/wm/tablet_mode/tablet_mode_event_handler.h"
-
-#include "ash/session/session_controller_impl.h"
-#include "ash/shell.h"
-#include "ash/wm/window_state.h"
-#include "ash/wm/window_util.h"
-#include "ash/wm/wm_event.h"
-#include "ui/aura/window.h"
-#include "ui/events/event.h"
-
-namespace ash {
-namespace {
-
-// The height of the area in which a touch operation leads to exiting the
-// full screen mode.
-const int kLeaveFullScreenAreaHeightInPixel = 2;
-
-}  // namespace
-
-TabletModeEventHandler::TabletModeEventHandler() {
-  Shell::Get()->AddPreTargetHandler(this);
-}
-
-TabletModeEventHandler::~TabletModeEventHandler() {
-  Shell::Get()->RemovePreTargetHandler(this);
-}
-
-void TabletModeEventHandler::OnTouchEvent(ui::TouchEvent* event) {
-  if (ToggleFullscreen(*event))
-    event->StopPropagation();
-}
-
-bool TabletModeEventHandler::ToggleFullscreen(const ui::TouchEvent& event) {
-  if (event.type() != ui::ET_TOUCH_PRESSED)
-    return false;
-
-  const SessionControllerImpl* controller = Shell::Get()->session_controller();
-
-  if (controller->IsScreenLocked() ||
-      controller->GetSessionState() != session_manager::SessionState::ACTIVE) {
-    return false;
-  }
-
-  // Find the active window (from the primary screen) to un-fullscreen.
-  aura::Window* window = window_util::GetActiveWindow();
-  if (!window)
-    return false;
-
-  WindowState* window_state = WindowState::Get(window);
-  if (!window_state->IsFullscreen() || window_state->IsInImmersiveFullscreen())
-    return false;
-
-  // Test that the touch happened in the top or bottom lines.
-  int y = event.y();
-  if (y >= kLeaveFullScreenAreaHeightInPixel &&
-      y < (window->bounds().height() - kLeaveFullScreenAreaHeightInPixel)) {
-    return false;
-  }
-
-  // Do not exit fullscreen in kiosk app mode.
-  if (Shell::Get()->session_controller()->IsRunningInAppMode())
-    return false;
-
-  WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN);
-  WindowState::Get(window)->OnWMEvent(&toggle_fullscreen);
-  return true;
-}
-
-}  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_event_handler.h b/ash/wm/tablet_mode/tablet_mode_event_handler.h
deleted file mode 100644
index a7b7a450cd278..0000000000000
--- a/ash/wm/tablet_mode/tablet_mode_event_handler.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_WM_TABLET_MODE_TABLET_MODE_EVENT_HANDLER_H_
-#define ASH_WM_TABLET_MODE_TABLET_MODE_EVENT_HANDLER_H_
-
-#include "base/macros.h"
-#include "ui/events/event_handler.h"
-
-namespace ui {
-class TouchEvent;
-}
-
-namespace ash {
-
-// TabletModeEventHandler handles toggling fullscreen when appropriate.
-// TabletModeEventHandler installs event handlers in an environment specific
-// way, e.g. EventHandler for aura.
-class TabletModeEventHandler : public ui::EventHandler {
- public:
-  TabletModeEventHandler();
-  ~TabletModeEventHandler() override;
-
- private:
-  // ui::EventHandler:
-  void OnTouchEvent(ui::TouchEvent* event) override;
-
-  // Returns true if a toggle happened.
-  bool ToggleFullscreen(const ui::TouchEvent& event);
-
-  DISALLOW_COPY_AND_ASSIGN(TabletModeEventHandler);
-};
-
-}  // namespace ash
-
-#endif  // ASH_WM_TABLET_MODE_TABLET_MODE_EVENT_HANDLER_H_
diff --git a/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.cc b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.cc
new file mode 100644
index 0000000000000..afa303638b03e
--- /dev/null
+++ b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.cc
@@ -0,0 +1,139 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h"
+
+#include "ash/public/cpp/shelf_config.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "ash/wm/wm_event.h"
+#include "ui/aura/window.h"
+#include "ui/events/event.h"
+
+namespace ash {
+namespace {
+
+// The height of the area in which a touch operation leads to exiting the
+// full screen mode.
+constexpr int kLeaveFullScreenAreaHeightInPixel = 2;
+
+}  // namespace
+
+TabletModeToggleFullscreenEventHandler::
+    TabletModeToggleFullscreenEventHandler() {
+  Shell::Get()->AddPreTargetHandler(this);
+}
+
+TabletModeToggleFullscreenEventHandler::
+    ~TabletModeToggleFullscreenEventHandler() {
+  ResetDragData();
+  Shell::Get()->RemovePreTargetHandler(this);
+}
+
+void TabletModeToggleFullscreenEventHandler::OnTouchEvent(
+    ui::TouchEvent* event) {
+  if (ProcessEvent(*event)) {
+    event->SetHandled();
+    event->StopPropagation();
+  }
+}
+
+void TabletModeToggleFullscreenEventHandler::OnWindowDestroying(
+    aura::Window* window) {
+  DCHECK(drag_data_);
+  DCHECK_EQ(drag_data_->window, window);
+  ResetDragData();
+}
+
+bool TabletModeToggleFullscreenEventHandler::ProcessEvent(
+    const ui::TouchEvent& event) {
+  switch (event.type()) {
+    case ui::ET_TOUCH_PRESSED: {
+      DCHECK(!drag_data_);
+
+      aura::Window* active_window = window_util::GetActiveWindow();
+      if (!active_window || !CanToggleFullscreen(active_window))
+        return false;
+
+      const int y = event.y();
+      // For touch press events only process the ones on the top or bottom
+      // lines.
+      if (y >= kLeaveFullScreenAreaHeightInPixel &&
+          y < (active_window->bounds().height() -
+               kLeaveFullScreenAreaHeightInPixel)) {
+        return false;
+      }
+
+      drag_data_ = DragData{y, active_window};
+      active_window->AddObserver(this);
+      return true;
+    }
+    case ui::ET_TOUCH_RELEASED: {
+      if (!drag_data_)
+        return false;
+
+      // Toggle fullscreen if dragged enough and the window can still be
+      // fullscreened.
+      const int drag_threshold =
+          ShelfConfig::Get()->shelf_size() *
+          ShelfConfig::Get()->drag_hide_ratio_threshold();
+      if (abs(event.y() - drag_data_->start_y_location) > drag_threshold &&
+          CanToggleFullscreen(drag_data_->window)) {
+        WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN);
+        WindowState::Get(drag_data_->window)->OnWMEvent(&toggle_fullscreen);
+      }
+
+      ResetDragData();
+      return true;
+    }
+    case ui::ET_TOUCH_MOVED:
+      return drag_data_.has_value();
+    case ui::ET_TOUCH_CANCELLED: {
+      const bool drag_in_progress = drag_data_.has_value();
+      ResetDragData();
+      return drag_in_progress;
+    }
+    default:
+      break;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+bool TabletModeToggleFullscreenEventHandler::CanToggleFullscreen(
+    const aura::Window* window) {
+  DCHECK(window);
+
+  const SessionControllerImpl* controller = Shell::Get()->session_controller();
+  if (controller->IsScreenLocked() ||
+      controller->GetSessionState() != session_manager::SessionState::ACTIVE) {
+    return false;
+  }
+
+  // Find the active window (from the primary screen) to un-fullscreen.
+  aura::Window* active_window = window_util::GetActiveWindow();
+  if (window != active_window)
+    return false;
+
+  const WindowState* window_state = WindowState::Get(window);
+  if (!window_state->IsFullscreen() || window_state->IsInImmersiveFullscreen())
+    return false;
+
+  // Do not exit fullscreen in kiosk app mode.
+  if (Shell::Get()->session_controller()->IsRunningInAppMode())
+    return false;
+
+  return true;
+}
+
+void TabletModeToggleFullscreenEventHandler::ResetDragData() {
+  if (drag_data_)
+    drag_data_->window->RemoveObserver(this);
+  drag_data_.reset();
+}
+
+}  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h
new file mode 100644
index 0000000000000..4ff560f00c8f8
--- /dev/null
+++ b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WM_TABLET_MODE_TABLET_MODE_TOGGLE_FULLSCREEN_EVENT_HANDLER_H_
+#define ASH_WM_TABLET_MODE_TABLET_MODE_TOGGLE_FULLSCREEN_EVENT_HANDLER_H_
+
+#include "ui/aura/window_observer.h"
+#include "ui/events/event_handler.h"
+
+namespace ui {
+class TouchEvent;
+}
+
+namespace ash {
+
+// TabletModeToggleFullscreenEventHandler handles toggling fullscreen when
+// appropriate. TabletModeToggleFullscreenEventHandler installs event handlers
+// in an environment specific way, e.g. EventHandler for aura.
+class TabletModeToggleFullscreenEventHandler : public ui::EventHandler,
+                                               public aura::WindowObserver {
+ public:
+  TabletModeToggleFullscreenEventHandler();
+  TabletModeToggleFullscreenEventHandler(
+      const TabletModeToggleFullscreenEventHandler&) = delete;
+  TabletModeToggleFullscreenEventHandler& operator=(
+      const TabletModeToggleFullscreenEventHandler&) = delete;
+  ~TabletModeToggleFullscreenEventHandler() override;
+
+ private:
+  struct DragData {
+    int start_y_location;
+    aura::Window* window;
+  };
+
+  // ui::EventHandler:
+  void OnTouchEvent(ui::TouchEvent* event) override;
+
+  // aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override;
+
+  bool ProcessEvent(const ui::TouchEvent& event);
+
+  // Returns true if |window| can be fullscreen toggled.
+  bool CanToggleFullscreen(const aura::Window* window);
+
+  // Resets |drag_data_| and remove the WindowObserver.
+  void ResetDragData();
+
+  // Valid if a processable drag is in progress. Contains the event initial
+  // location and the window that was active when the drag started.
+  base::Optional<DragData> drag_data_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_TABLET_MODE_TABLET_MODE_TOGGLE_FULLSCREEN_EVENT_HANDLER_H_
diff --git a/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc
new file mode 100644
index 0000000000000..c369eb7257d93
--- /dev/null
+++ b/ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler_unittest.cc
@@ -0,0 +1,171 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h"
+
+#include "ash/public/cpp/window_properties.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/wm_event.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+
+class TabletModeToggleFullscreenEventHandlerTest : public AshTestBase {
+ public:
+  TabletModeToggleFullscreenEventHandlerTest() = default;
+  TabletModeToggleFullscreenEventHandlerTest(
+      const TabletModeToggleFullscreenEventHandlerTest&) = delete;
+  TabletModeToggleFullscreenEventHandlerTest& operator=(
+      const TabletModeToggleFullscreenEventHandlerTest&) = delete;
+  ~TabletModeToggleFullscreenEventHandlerTest() override = default;
+
+  // AshTestBase:
+  void SetUp() override {
+    AshTestBase::SetUp();
+
+    UpdateDisplay("800x600");
+    TabletModeControllerTestApi().EnterTabletMode();
+
+    background_window_ = CreateTestWindow(gfx::Rect(200, 200));
+    foreground_window_ = CreateTestWindow(gfx::Rect(200, 200));
+    ToggleFullscreen(foreground_window_.get(), /*immersive=*/false);
+    ToggleFullscreen(background_window_.get(), /*immersive=*/false);
+  }
+
+  void TearDown() override {
+    background_window_.reset();
+    foreground_window_.reset();
+    AshTestBase::TearDown();
+  }
+
+  void ToggleFullscreen(aura::Window* window, bool immersive) {
+    WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN);
+    WindowState::Get(window)->OnWMEvent(&toggle_fullscreen);
+    window->SetProperty(kImmersiveIsActive, immersive);
+  }
+
+  bool IsFullscreen(aura::Window* window) const {
+    return WindowState::Get(window)->IsFullscreen();
+  }
+
+  void GenerateSwipe(int start_y, int end_y) {
+    GetEventGenerator()->GestureScrollSequence(
+        gfx::Point(400, start_y), gfx::Point(400, end_y),
+        base::TimeDelta::FromMilliseconds(100), 3);
+  }
+
+  aura::Window* foreground_window() { return foreground_window_.get(); }
+  aura::Window* background_window() { return background_window_.get(); }
+
+ private:
+  std::unique_ptr<aura::Window> foreground_window_;
+  std::unique_ptr<aura::Window> background_window_;
+};
+
+TEST_F(TabletModeToggleFullscreenEventHandlerTest, SwipeFromTop) {
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+  ASSERT_TRUE(IsFullscreen(background_window()));
+
+  // Try swiping from a point not on the edge. Verify that we do not exit
+  // fullscreen.
+  GenerateSwipe(100, 200);
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+  ASSERT_TRUE(IsFullscreen(background_window()));
+
+  // Try a tiny swipe that is on the edge. Verify that we do not exit
+  // fullscreen.
+  GenerateSwipe(1, 5);
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+  ASSERT_TRUE(IsFullscreen(background_window()));
+
+  // Test that a normal swipe on the edge will exit fullscreen on the active
+  // window.
+  GenerateSwipe(1, 50);
+  EXPECT_FALSE(IsFullscreen(foreground_window()));
+  EXPECT_TRUE(IsFullscreen(background_window()));
+
+  // Test that a second swipe will not do anything..
+  GenerateSwipe(1, 50);
+  EXPECT_FALSE(IsFullscreen(foreground_window()));
+  EXPECT_TRUE(IsFullscreen(background_window()));
+}
+
+TEST_F(TabletModeToggleFullscreenEventHandlerTest, SwipeFromBottom) {
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+  ASSERT_TRUE(IsFullscreen(background_window()));
+
+  // Try swiping from a point not on the edge. Verify that we do not exit
+  // fullscreen.
+  GenerateSwipe(500, 400);
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+  ASSERT_TRUE(IsFullscreen(background_window()));
+
+  // Try a tiny swipe that is on the edge. Verify that we do not exit
+  // fullscreen.
+  GenerateSwipe(599, 594);
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+  ASSERT_TRUE(IsFullscreen(background_window()));
+
+  // Test that a normal swipe on the edge will exit fullscreen on the active
+  // window.
+  GenerateSwipe(599, 549);
+  EXPECT_FALSE(IsFullscreen(foreground_window()));
+  EXPECT_TRUE(IsFullscreen(background_window()));
+}
+
+// Tests that tapping on the edge does not exit fullscreen.
+TEST_F(TabletModeToggleFullscreenEventHandlerTest, TapOnEdge) {
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+
+  // Tap on the top edge.
+  GetEventGenerator()->set_current_screen_location(gfx::Point(400, 1));
+  GetEventGenerator()->PressTouch();
+  GetEventGenerator()->ReleaseTouch();
+  EXPECT_TRUE(IsFullscreen(foreground_window()));
+
+  // Tap on the bottom edge.
+  GetEventGenerator()->set_current_screen_location(gfx::Point(400, 50));
+  GetEventGenerator()->PressTouch();
+  GetEventGenerator()->ReleaseTouch();
+  EXPECT_TRUE(IsFullscreen(foreground_window()));
+}
+
+TEST_F(TabletModeToggleFullscreenEventHandlerTest,
+       SwipeImmersiveFullscreenWindow) {
+  // Switch from non-immersive fullscreen to immersive fullscreen mode.
+  ToggleFullscreen(foreground_window(), /*immersive=*/true);
+  ToggleFullscreen(foreground_window(), /*immersive=*/true);
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+
+  // Test that a normal swipe on the top edge will not exit immersive
+  // fullscreen.
+  GenerateSwipe(1, 50);
+  EXPECT_TRUE(IsFullscreen(foreground_window()));
+  EXPECT_TRUE(IsFullscreen(background_window()));
+
+  // Test that a normal swipe on the top edge will not exit immersive
+  // fullscreen.
+  GenerateSwipe(599, 549);
+  EXPECT_TRUE(IsFullscreen(foreground_window()));
+  EXPECT_TRUE(IsFullscreen(background_window()));
+}
+
+// Tests that if a window is un-fullscreened during a drag, it remains
+// un-fullscreened on touch release.
+TEST_F(TabletModeToggleFullscreenEventHandlerTest, ToggleFullscreenDuringDrag) {
+  ASSERT_TRUE(IsFullscreen(foreground_window()));
+
+  GetEventGenerator()->set_current_screen_location(gfx::Point(400, 1));
+  GetEventGenerator()->PressTouch();
+  ToggleFullscreen(foreground_window(), /*immersive=*/false);
+  EXPECT_FALSE(IsFullscreen(foreground_window()));
+
+  GetEventGenerator()->set_current_screen_location(gfx::Point(400, 50));
+  GetEventGenerator()->ReleaseTouch();
+  EXPECT_FALSE(IsFullscreen(foreground_window()));
+}
+
+}  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.cc b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
index b144ef8a86d48..94cc436ebfc09 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.cc
@@ -23,7 +23,7 @@
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/splitview/split_view_utils.h"
 #include "ash/wm/tablet_mode/scoped_skip_user_session_blocked_check.h"
-#include "ash/wm/tablet_mode/tablet_mode_event_handler.h"
+#include "ash/wm/tablet_mode/tablet_mode_toggle_fullscreen_event_handler.h"
 #include "ash/wm/tablet_mode/tablet_mode_window_state.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -158,7 +158,7 @@ void TabletModeWindowManager::Init() {
   Shell::Get()->overview_controller()->AddObserver(this);
   accounts_since_entering_tablet_.insert(
       Shell::Get()->session_controller()->GetActiveAccountId());
-  event_handler_ = std::make_unique<TabletModeEventHandler>();
+  event_handler_ = std::make_unique<TabletModeToggleFullscreenEventHandler>();
 }
 
 void TabletModeWindowManager::Shutdown() {
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager.h b/ash/wm/tablet_mode/tablet_mode_window_manager.h
index 4666ea88f704c..cb1b3726bb051 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager.h
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager.h
@@ -29,7 +29,7 @@ class Window;
 
 namespace ash {
 class TabletModeController;
-class TabletModeEventHandler;
+class TabletModeToggleFullscreenEventHandler;
 class TabletModeWindowState;
 
 // A window manager which - when created - will force all windows into maximized
@@ -184,7 +184,7 @@ class ASH_EXPORT TabletModeWindowManager : public aura::WindowObserver,
   // All accounts that have been active at least once since tablet mode started.
   base::flat_set<AccountId> accounts_since_entering_tablet_;
 
-  std::unique_ptr<TabletModeEventHandler> event_handler_;
+  std::unique_ptr<TabletModeToggleFullscreenEventHandler> event_handler_;
 
   // True when tablet mode is about to end.
   bool is_exiting_ = false;
diff --git a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
index d44f3754426a3..e0ae20897a713 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_manager_unittest.cc
@@ -1318,215 +1318,6 @@ TEST_P(TabletModeWindowManagerTest, TryToDesktopSizeDragUnmaximizable) {
   EXPECT_EQ(first_dragged_origin.y() + 5, window->bounds().y());
 }
 
-// Test that an edge swipe from the top will end full screen mode.
-TEST_P(TabletModeWindowManagerTest, ExitFullScreenWithEdgeSwipeFromTop) {
-  gfx::Rect rect(10, 10, 200, 50);
-  std::unique_ptr<aura::Window> background_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  std::unique_ptr<aura::Window> foreground_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  WindowState* background_window_state =
-      WindowState::Get(background_window.get());
-  WindowState* foreground_window_state =
-      WindowState::Get(foreground_window.get());
-  wm::ActivateWindow(foreground_window.get());
-  CreateTabletModeWindowManager();
-
-  // Fullscreen both windows.
-  WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
-  background_window_state->OnWMEvent(&event);
-  foreground_window_state->OnWMEvent(&event);
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-  EXPECT_TRUE(foreground_window_state->IsFullscreen());
-  EXPECT_EQ(foreground_window.get(), window_util::GetActiveWindow());
-
-  // Do an edge swipe top into screen.
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-  generator.GestureScrollSequence(gfx::Point(50, 0), gfx::Point(50, 100),
-                                  base::TimeDelta::FromMilliseconds(20), 10);
-
-  EXPECT_FALSE(foreground_window_state->IsFullscreen());
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-
-  // Do a second edge swipe top into screen.
-  generator.GestureScrollSequence(gfx::Point(50, 0), gfx::Point(50, 100),
-                                  base::TimeDelta::FromMilliseconds(20), 10);
-
-  EXPECT_FALSE(foreground_window_state->IsFullscreen());
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-
-  DestroyTabletModeWindowManager();
-}
-
-// Test that an edge swipe from the bottom will end full screen mode.
-TEST_P(TabletModeWindowManagerTest, ExitFullScreenWithEdgeSwipeFromBottom) {
-  gfx::Rect rect(10, 10, 200, 50);
-  std::unique_ptr<aura::Window> background_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  std::unique_ptr<aura::Window> foreground_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  WindowState* background_window_state =
-      WindowState::Get(background_window.get());
-  WindowState* foreground_window_state =
-      WindowState::Get(foreground_window.get());
-  wm::ActivateWindow(foreground_window.get());
-  CreateTabletModeWindowManager();
-
-  // Fullscreen both windows.
-  WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
-  background_window_state->OnWMEvent(&event);
-  foreground_window_state->OnWMEvent(&event);
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-  EXPECT_TRUE(foreground_window_state->IsFullscreen());
-  EXPECT_EQ(foreground_window.get(), window_util::GetActiveWindow());
-
-  // Do an edge swipe bottom into screen.
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-  int y = Shell::GetPrimaryRootWindow()->bounds().bottom();
-  generator.GestureScrollSequence(gfx::Point(50, y), gfx::Point(50, y - 100),
-                                  base::TimeDelta::FromMilliseconds(20), 10);
-
-  EXPECT_FALSE(foreground_window_state->IsFullscreen());
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-
-  DestroyTabletModeWindowManager();
-}
-
-// Test that an edge touch press at the top will end full screen mode.
-TEST_P(TabletModeWindowManagerTest, ExitFullScreenWithEdgeTouchAtTop) {
-  gfx::Rect rect(10, 10, 200, 50);
-  std::unique_ptr<aura::Window> background_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  std::unique_ptr<aura::Window> foreground_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  WindowState* background_window_state =
-      WindowState::Get(background_window.get());
-  WindowState* foreground_window_state =
-      WindowState::Get(foreground_window.get());
-  wm::ActivateWindow(foreground_window.get());
-  CreateTabletModeWindowManager();
-
-  // Fullscreen both windows.
-  WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
-  background_window_state->OnWMEvent(&event);
-  foreground_window_state->OnWMEvent(&event);
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-  EXPECT_TRUE(foreground_window_state->IsFullscreen());
-  EXPECT_EQ(foreground_window.get(), window_util::GetActiveWindow());
-
-  // Touch tap on the top edge.
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-  generator.GestureTapAt(gfx::Point(100, 0));
-  EXPECT_FALSE(foreground_window_state->IsFullscreen());
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-
-  // Try the same again and see that nothing changes.
-  generator.GestureTapAt(gfx::Point(100, 0));
-  EXPECT_FALSE(foreground_window_state->IsFullscreen());
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-
-  DestroyTabletModeWindowManager();
-}
-
-// Test that an edge touch press at the bottom will end full screen mode.
-TEST_P(TabletModeWindowManagerTest, ExitFullScreenWithEdgeTouchAtBottom) {
-  gfx::Rect rect(10, 10, 200, 50);
-  std::unique_ptr<aura::Window> background_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  std::unique_ptr<aura::Window> foreground_window(
-      CreateWindow(aura::client::WINDOW_TYPE_NORMAL, rect));
-  WindowState* background_window_state =
-      WindowState::Get(background_window.get());
-  WindowState* foreground_window_state =
-      WindowState::Get(foreground_window.get());
-  wm::ActivateWindow(foreground_window.get());
-  CreateTabletModeWindowManager();
-
-  // Fullscreen both windows.
-  WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
-  background_window_state->OnWMEvent(&event);
-  foreground_window_state->OnWMEvent(&event);
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-  EXPECT_TRUE(foreground_window_state->IsFullscreen());
-  EXPECT_EQ(foreground_window.get(), window_util::GetActiveWindow());
-
-  // Touch tap on the bottom edge.
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-  generator.GestureTapAt(
-      gfx::Point(100, Shell::GetPrimaryRootWindow()->bounds().bottom() - 1));
-  EXPECT_FALSE(foreground_window_state->IsFullscreen());
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-
-  // Try the same again and see that nothing changes.
-  generator.GestureTapAt(
-      gfx::Point(100, Shell::GetPrimaryRootWindow()->bounds().bottom() - 1));
-  EXPECT_FALSE(foreground_window_state->IsFullscreen());
-  EXPECT_TRUE(background_window_state->IsFullscreen());
-
-  DestroyTabletModeWindowManager();
-}
-
-// Test that an edge swipe from the top on an immersive mode window will not end
-// full screen mode.
-TEST_P(TabletModeWindowManagerTest, NoExitImmersiveModeWithEdgeSwipeFromTop) {
-  std::unique_ptr<aura::Window> window(CreateWindow(
-      aura::client::WINDOW_TYPE_NORMAL, gfx::Rect(10, 10, 200, 50)));
-  WindowState* window_state = WindowState::Get(window.get());
-  wm::ActivateWindow(window.get());
-  CreateTabletModeWindowManager();
-
-  // Fullscreen the window.
-  WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
-  window_state->OnWMEvent(&event);
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_FALSE(window_state->IsInImmersiveFullscreen());
-  EXPECT_EQ(window.get(), window_util::GetActiveWindow());
-
-  window->SetProperty(kImmersiveIsActive, true);
-
-  // Do an edge swipe top into screen.
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-  generator.GestureScrollSequence(gfx::Point(50, 0), gfx::Point(50, 100),
-                                  base::TimeDelta::FromMilliseconds(20), 10);
-
-  // It should have not exited full screen or immersive mode.
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_TRUE(window_state->IsInImmersiveFullscreen());
-
-  DestroyTabletModeWindowManager();
-}
-
-// Test that an edge swipe from the bottom will not end immersive mode.
-TEST_P(TabletModeWindowManagerTest,
-       NoExitImmersiveModeWithEdgeSwipeFromBottom) {
-  std::unique_ptr<aura::Window> window(CreateWindow(
-      aura::client::WINDOW_TYPE_NORMAL, gfx::Rect(10, 10, 200, 50)));
-  WindowState* window_state = WindowState::Get(window.get());
-  wm::ActivateWindow(window.get());
-  CreateTabletModeWindowManager();
-
-  // Fullscreen the window.
-  WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
-  window_state->OnWMEvent(&event);
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_FALSE(window_state->IsInImmersiveFullscreen());
-  EXPECT_EQ(window.get(), window_util::GetActiveWindow());
-  window->SetProperty(kImmersiveIsActive, true);
-  EXPECT_TRUE(window_state->IsInImmersiveFullscreen());
-
-  // Do an edge swipe bottom into screen.
-  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
-  int y = Shell::GetPrimaryRootWindow()->bounds().bottom();
-  generator.GestureScrollSequence(gfx::Point(50, y), gfx::Point(50, y - 100),
-                                  base::TimeDelta::FromMilliseconds(20), 10);
-
-  // The window should still be full screen and immersive.
-  EXPECT_TRUE(window_state->IsFullscreen());
-  EXPECT_TRUE(window_state->IsInImmersiveFullscreen());
-
-  DestroyTabletModeWindowManager();
-}
-
 // Tests that windows with the always-on-top property are not managed by
 // the TabletModeWindowManager while tablet mode is engaged (i.e.,
 // they remain free-floating).