diff options
Diffstat (limited to 'content/browser/renderer_host')
5 files changed, 995 insertions, 84 deletions
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc new file mode 100644 index 0000000..ada3e9f --- /dev/null +++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc @@ -0,0 +1,353 @@ +// Copyright 2015 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 "content/browser/renderer_host/input/touch_selection_controller_client_aura.h" + +#include "content/browser/renderer_host/render_widget_host_delegate.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/browser/renderer_host/render_widget_host_view_aura.h" +#include "content/common/view_messages.h" +#include "content/public/browser/render_view_host.h" +#include "ui/aura/client/cursor_client.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/strings/grit/ui_strings.h" +#include "ui/touch_selection/touch_handle_drawable_aura.h" +#include "ui/touch_selection/touch_selection_menu_runner.h" + +namespace content { +namespace { + +// Delay before showing the quick menu, in milliseconds. +const int kQuickMenuDelayInMs = 100; + +gfx::Rect ConvertRectToScreen(aura::Window* window, const gfx::RectF& rect) { + gfx::Point origin = gfx::ToRoundedPoint(rect.origin()); + gfx::Point bottom_right = gfx::ToRoundedPoint(rect.bottom_right()); + + aura::Window* root_window = window->GetRootWindow(); + if (root_window) { + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(root_window); + if (screen_position_client) { + screen_position_client->ConvertPointToScreen(window, &origin); + screen_position_client->ConvertPointToScreen(window, &bottom_right); + } + } + return gfx::Rect(origin.x(), origin.y(), bottom_right.x() - origin.x(), + bottom_right.y() - origin.y()); +} + +} // namespace + +// A pre-target event handler for aura::Env which deactivates touch selection on +// mouse and keyboard events. +class TouchSelectionControllerClientAura::EnvPreTargetHandler + : public ui::EventHandler { + public: + EnvPreTargetHandler(ui::TouchSelectionController* selection_controller, + aura::Window* window); + ~EnvPreTargetHandler() override; + + private: + // EventHandler: + void OnKeyEvent(ui::KeyEvent* event) override; + void OnMouseEvent(ui::MouseEvent* event) override; + void OnScrollEvent(ui::ScrollEvent* event) override; + + ui::TouchSelectionController* selection_controller_; + aura::Window* window_; + + DISALLOW_COPY_AND_ASSIGN(EnvPreTargetHandler); +}; + +TouchSelectionControllerClientAura::EnvPreTargetHandler::EnvPreTargetHandler( + ui::TouchSelectionController* selection_controller, + aura::Window* window) + : selection_controller_(selection_controller), window_(window) { + aura::Env::GetInstance()->AddPreTargetHandler(this); +} + +TouchSelectionControllerClientAura::EnvPreTargetHandler:: + ~EnvPreTargetHandler() { + aura::Env::GetInstance()->RemovePreTargetHandler(this); +} + +void TouchSelectionControllerClientAura::EnvPreTargetHandler::OnKeyEvent( + ui::KeyEvent* event) { + DCHECK_NE(ui::TouchSelectionController::INACTIVE, + selection_controller_->active_status()); + + selection_controller_->HideAndDisallowShowingAutomatically(); +} + +void TouchSelectionControllerClientAura::EnvPreTargetHandler::OnMouseEvent( + ui::MouseEvent* event) { + DCHECK_NE(ui::TouchSelectionController::INACTIVE, + selection_controller_->active_status()); + + // If mouse events are not enabled, this mouse event is synthesized from a + // touch event in which case we don't want to deactivate touch selection. + aura::client::CursorClient* cursor_client = + aura::client::GetCursorClient(window_->GetRootWindow()); + if (!cursor_client || cursor_client->IsMouseEventsEnabled()) + selection_controller_->HideAndDisallowShowingAutomatically(); +} + +void TouchSelectionControllerClientAura::EnvPreTargetHandler::OnScrollEvent( + ui::ScrollEvent* event) { + DCHECK_NE(ui::TouchSelectionController::INACTIVE, + selection_controller_->active_status()); + + selection_controller_->HideAndDisallowShowingAutomatically(); +} + +TouchSelectionControllerClientAura::TouchSelectionControllerClientAura( + RenderWidgetHostViewAura* rwhva) + : rwhva_(rwhva), + quick_menu_timer_( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kQuickMenuDelayInMs), + base::Bind(&TouchSelectionControllerClientAura::ShowQuickMenu, + base::Unretained(this)), + false), + touch_down_(false), + scroll_in_progress_(false), + handle_drag_in_progress_(false) { + DCHECK(rwhva_); +} + +TouchSelectionControllerClientAura::~TouchSelectionControllerClientAura() { +} + +void TouchSelectionControllerClientAura::OnWindowMoved() { + UpdateQuickMenu(); +} + +void TouchSelectionControllerClientAura::OnTouchDown() { + touch_down_ = true; + UpdateQuickMenu(); +} + +void TouchSelectionControllerClientAura::OnTouchUp() { + touch_down_ = false; + UpdateQuickMenu(); +} + +void TouchSelectionControllerClientAura::OnScrollStarted() { + scroll_in_progress_ = true; + rwhva_->selection_controller()->SetTemporarilyHidden(true); + UpdateQuickMenu(); +} + +void TouchSelectionControllerClientAura::OnScrollCompleted() { + scroll_in_progress_ = false; + rwhva_->selection_controller()->SetTemporarilyHidden(false); + UpdateQuickMenu(); +} + +bool TouchSelectionControllerClientAura::IsQuickMenuAllowed() const { + return !touch_down_ && !scroll_in_progress_ && !handle_drag_in_progress_; +} + +void TouchSelectionControllerClientAura::ShowQuickMenu() { + if (!ui::TouchSelectionMenuRunner::GetInstance()) + return; + + gfx::RectF rect = rwhva_->selection_controller()->GetRectBetweenBounds(); + + // Clip rect, which is in |rwhva_|'s window's coordinate space, to client + // bounds. + gfx::PointF origin = rect.origin(); + gfx::PointF bottom_right = rect.bottom_right(); + gfx::Rect client_bounds = rwhva_->GetNativeView()->bounds(); + origin.SetToMax(client_bounds.origin()); + bottom_right.SetToMin(client_bounds.bottom_right()); + if (origin.x() > bottom_right.x() || origin.y() > bottom_right.y()) + return; + + gfx::Vector2dF diagonal = bottom_right - origin; + gfx::SizeF size(diagonal.x(), diagonal.y()); + gfx::RectF anchor_rect(origin, size); + + // Calculate maximum handle image size; + gfx::SizeF max_handle_size = + rwhva_->selection_controller()->GetStartHandleRect().size(); + max_handle_size.SetToMax( + rwhva_->selection_controller()->GetEndHandleRect().size()); + + aura::Window* parent = rwhva_->GetNativeView(); + ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu( + this, ConvertRectToScreen(parent, anchor_rect), + gfx::ToRoundedSize(max_handle_size), parent->GetToplevelWindow()); +} + +void TouchSelectionControllerClientAura::UpdateQuickMenu() { + bool menu_is_showing = + ui::TouchSelectionMenuRunner::GetInstance() && + ui::TouchSelectionMenuRunner::GetInstance()->IsRunning(); + bool menu_should_show = rwhva_->selection_controller()->active_status() != + ui::TouchSelectionController::INACTIVE && + IsQuickMenuAllowed(); + + // Hide the quick menu if there is any. This should happen even if the menu + // should be shown again, in order to update its location or content. + if (menu_is_showing) + ui::TouchSelectionMenuRunner::GetInstance()->CloseMenu(); + else + quick_menu_timer_.Stop(); + + // Start timer to show quick menu if necessary. + if (menu_should_show) { + if (show_quick_menu_immediately_for_test_) + ShowQuickMenu(); + else + quick_menu_timer_.Reset(); + } +} + +bool TouchSelectionControllerClientAura::SupportsAnimation() const { + return false; +} + +void TouchSelectionControllerClientAura::SetNeedsAnimate() { + NOTREACHED(); +} + +void TouchSelectionControllerClientAura::MoveCaret( + const gfx::PointF& position) { + RenderWidgetHostImpl* host = + RenderWidgetHostImpl::From(rwhva_->GetRenderWidgetHost()); + host->MoveCaret(gfx::ToRoundedPoint(position)); +} + +void TouchSelectionControllerClientAura::MoveRangeSelectionExtent( + const gfx::PointF& extent) { + RenderWidgetHostDelegate* host_delegate = + RenderWidgetHostImpl::From(rwhva_->GetRenderWidgetHost())->delegate(); + if (host_delegate) + host_delegate->MoveRangeSelectionExtent(gfx::ToRoundedPoint(extent)); +} + +void TouchSelectionControllerClientAura::SelectBetweenCoordinates( + const gfx::PointF& base, + const gfx::PointF& extent) { + RenderWidgetHostDelegate* host_delegate = + RenderWidgetHostImpl::From(rwhva_->GetRenderWidgetHost())->delegate(); + if (host_delegate) { + host_delegate->SelectRange(gfx::ToRoundedPoint(base), + gfx::ToRoundedPoint(extent)); + } +} + +void TouchSelectionControllerClientAura::OnSelectionEvent( + ui::SelectionEventType event) { + switch (event) { + case ui::SELECTION_HANDLES_SHOWN: + case ui::INSERTION_HANDLE_SHOWN: + UpdateQuickMenu(); + env_pre_target_handler_.reset(new EnvPreTargetHandler( + rwhva_->selection_controller(), rwhva_->GetNativeView())); + break; + case ui::SELECTION_HANDLES_CLEARED: + case ui::INSERTION_HANDLE_CLEARED: + env_pre_target_handler_.reset(); + UpdateQuickMenu(); + break; + case ui::SELECTION_HANDLE_DRAG_STARTED: + case ui::INSERTION_HANDLE_DRAG_STARTED: + handle_drag_in_progress_ = true; + UpdateQuickMenu(); + break; + case ui::SELECTION_HANDLE_DRAG_STOPPED: + case ui::INSERTION_HANDLE_DRAG_STOPPED: + handle_drag_in_progress_ = false; + UpdateQuickMenu(); + break; + case ui::SELECTION_HANDLES_MOVED: + case ui::INSERTION_HANDLE_MOVED: + UpdateQuickMenu(); + break; + case ui::INSERTION_HANDLE_TAPPED: + case ui::SELECTION_ESTABLISHED: + case ui::SELECTION_DISSOLVED: + break; + }; +} + +scoped_ptr<ui::TouchHandleDrawable> +TouchSelectionControllerClientAura::CreateDrawable() { + return scoped_ptr<ui::TouchHandleDrawable>( + new ui::TouchHandleDrawableAura(rwhva_->GetNativeView())); +} + +bool TouchSelectionControllerClientAura::IsCommandIdEnabled( + int command_id) const { + bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE; + bool readable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD; + gfx::Range selection_range; + rwhva_->GetSelectionRange(&selection_range); + bool has_selection = !selection_range.is_empty(); + switch (command_id) { + case IDS_APP_CUT: + return editable && readable && has_selection; + case IDS_APP_COPY: + return readable && has_selection; + case IDS_APP_PASTE: { + base::string16 result; + ui::Clipboard::GetForCurrentThread()->ReadText( + ui::CLIPBOARD_TYPE_COPY_PASTE, &result); + return editable && !result.empty(); + } + default: + return false; + } +} + +void TouchSelectionControllerClientAura::ExecuteCommand(int command_id, + int event_flags) { + rwhva_->selection_controller()->HideAndDisallowShowingAutomatically(); + RenderWidgetHostDelegate* host_delegate = + RenderWidgetHostImpl::From(rwhva_->GetRenderWidgetHost())->delegate(); + if (!host_delegate) + return; + + switch (command_id) { + case IDS_APP_CUT: + host_delegate->Cut(); + break; + case IDS_APP_COPY: + host_delegate->Copy(); + break; + case IDS_APP_PASTE: + host_delegate->Paste(); + break; + default: + NOTREACHED(); + break; + } +} + +void TouchSelectionControllerClientAura::RunContextMenu() { + gfx::RectF anchor_rect = + rwhva_->selection_controller()->GetRectBetweenBounds(); + gfx::PointF anchor_point = + gfx::PointF(anchor_rect.CenterPoint().x(), anchor_rect.y()); + RenderWidgetHostImpl* host = + RenderWidgetHostImpl::From(rwhva_->GetRenderWidgetHost()); + host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), + ui::MENU_SOURCE_TOUCH_EDIT_MENU, + gfx::ToRoundedPoint(anchor_point))); + + // Hide selection handles after getting rect-between-bounds from touch + // selection controller; otherwise, rect would be empty and the above + // calculations would be invalid. + rwhva_->selection_controller()->HideAndDisallowShowingAutomatically(); +} + +} // namespace content diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura.h b/content/browser/renderer_host/input/touch_selection_controller_client_aura.h new file mode 100644 index 0000000..bb9dd66 --- /dev/null +++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura.h @@ -0,0 +1,80 @@ +// Copyright 2015 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 CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_SELECTION_CONTROLLER_CLIENT_AURA_H_ +#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_SELECTION_CONTROLLER_CLIENT_AURA_H_ + +#include "base/timer/timer.h" +#include "content/common/content_export.h" +#include "ui/touch_selection/touch_selection_controller.h" +#include "ui/touch_selection/touch_selection_menu_runner.h" + +namespace content { +class RenderWidgetHostViewAura; + +// An implementation of |TouchSelectionControllerClient| to be used in Aura's +// implementation of touch selection for contents. +class CONTENT_EXPORT TouchSelectionControllerClientAura + : public ui::TouchSelectionControllerClient, + public ui::TouchSelectionMenuClient { + public: + explicit TouchSelectionControllerClientAura(RenderWidgetHostViewAura* rwhva); + ~TouchSelectionControllerClientAura() override; + + // Called when |rwhva_|'s window is moved, to update the quick menu's + // position. + void OnWindowMoved(); + + // Called on first touch down/last touch up to hide/show the quick menu. + void OnTouchDown(); + void OnTouchUp(); + + // Called when touch scroll starts/completes to hide/show touch handles and + // the quick menu. + void OnScrollStarted(); + void OnScrollCompleted(); + + private: + friend class TestTouchSelectionControllerClientAura; + class EnvPreTargetHandler; + + bool IsQuickMenuAllowed() const; + void ShowQuickMenu(); + void UpdateQuickMenu(); + + // ui::TouchSelectionControllerClient: + bool SupportsAnimation() const override; + void SetNeedsAnimate() override; + void MoveCaret(const gfx::PointF& position) override; + void MoveRangeSelectionExtent(const gfx::PointF& extent) override; + void SelectBetweenCoordinates(const gfx::PointF& base, + const gfx::PointF& extent) override; + void OnSelectionEvent(ui::SelectionEventType event) override; + scoped_ptr<ui::TouchHandleDrawable> CreateDrawable() override; + + // ui::TouchSelectionMenuClient: + bool IsCommandIdEnabled(int command_id) const override; + void ExecuteCommand(int command_id, int event_flags) override; + void RunContextMenu() override; + + // Not owned, non-null for the lifetime of this object. + RenderWidgetHostViewAura* rwhva_; + + base::Timer quick_menu_timer_; + bool touch_down_; + bool scroll_in_progress_; + bool handle_drag_in_progress_; + + bool show_quick_menu_immediately_for_test_; + + // A pre-target event handler for aura::Env which deactivates touch selection + // on mouse and keyboard events. + scoped_ptr<EnvPreTargetHandler> env_pre_target_handler_; + + DISALLOW_COPY_AND_ASSIGN(TouchSelectionControllerClientAura); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_SELECTION_CONTROLLER_CLIENT_AURA_H_ diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc new file mode 100644 index 0000000..999b1cd --- /dev/null +++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc @@ -0,0 +1,441 @@ +// Copyright 2015 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 "content/browser/renderer_host/input/touch_selection_controller_client_aura.h" + +#include "base/json/json_reader.h" +#include "base/run_loop.h" +#include "content/browser/renderer_host/render_widget_host_view_aura.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/shell/browser/shell.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/events/event_utils.h" +#include "ui/events/test/event_generator.h" +#include "ui/touch_selection/touch_selection_controller_test_api.h" + +namespace content { +namespace { + +bool JSONToPoint(const std::string& str, gfx::PointF* point) { + scoped_ptr<base::Value> value = base::JSONReader::Read(str); + if (!value) + return false; + base::DictionaryValue* root; + if (!value->GetAsDictionary(&root)) + return false; + double x, y; + if (!root->GetDouble("x", &x)) + return false; + if (!root->GetDouble("y", &y)) + return false; + point->set_x(x); + point->set_y(y); + return true; +} + +// A mock touch selection menu runner to use whenever a default one is not +// installed. +class TestTouchSelectionMenuRunner : public ui::TouchSelectionMenuRunner { + public: + TestTouchSelectionMenuRunner() : menu_opened_(false) {} + ~TestTouchSelectionMenuRunner() override {} + + private: + void OpenMenu(ui::TouchSelectionMenuClient* client, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, + aura::Window* context) override { + menu_opened_ = true; + } + + void CloseMenu() override { menu_opened_ = false; } + + bool IsRunning() const override { return menu_opened_; } + + bool menu_opened_; + + DISALLOW_COPY_AND_ASSIGN(TestTouchSelectionMenuRunner); +}; + +} // namespace + +class TestTouchSelectionControllerClientAura + : public TouchSelectionControllerClientAura { + public: + explicit TestTouchSelectionControllerClientAura( + RenderWidgetHostViewAura* rwhva) + : TouchSelectionControllerClientAura(rwhva), + expected_event_(ui::SELECTION_HANDLES_SHOWN) { + show_quick_menu_immediately_for_test_ = true; + } + + ~TestTouchSelectionControllerClientAura() override {} + + void InitWaitForSelectionEvent(ui::SelectionEventType expected_event) { + DCHECK(!run_loop_); + expected_event_ = expected_event; + run_loop_.reset(new base::RunLoop()); + } + + void Wait() { + DCHECK(run_loop_); + run_loop_->Run(); + run_loop_.reset(); + } + + private: + // TouchSelectionControllerClientAura: + void OnSelectionEvent(ui::SelectionEventType event) override { + TouchSelectionControllerClientAura::OnSelectionEvent(event); + if (run_loop_ && event == expected_event_) + run_loop_->Quit(); + } + + bool IsCommandIdEnabled(int command_id) const override { + // Return true so that quick menu has something to show. + return true; + } + + ui::SelectionEventType expected_event_; + scoped_ptr<base::RunLoop> run_loop_; + + DISALLOW_COPY_AND_ASSIGN(TestTouchSelectionControllerClientAura); +}; + +class TouchSelectionControllerClientAuraTest : public ContentBrowserTest { + public: + TouchSelectionControllerClientAuraTest() {} + ~TouchSelectionControllerClientAuraTest() override {} + + protected: + // Starts the test server and navigates to the given url. Sets a large enough + // size to the root window. Returns after the navigation to the url is + // complete. + void StartTestWithPage(const std::string& url) { + ASSERT_TRUE(test_server()->Start()); + GURL test_url(test_server()->GetURL(url)); + NavigateToURL(shell(), test_url); + aura::Window* content = shell()->web_contents()->GetContentNativeView(); + content->GetHost()->SetBounds(gfx::Rect(800, 600)); + } + + bool GetPointInsideText(gfx::PointF* point) { + std::string str; + if (ExecuteScriptAndExtractString(shell()->web_contents()->GetMainFrame(), + "get_point_inside_text()", &str)) { + return JSONToPoint(str, point); + } + return false; + } + + bool GetPointInsideTextfield(gfx::PointF* point) { + std::string str; + if (ExecuteScriptAndExtractString(shell()->web_contents()->GetMainFrame(), + "get_point_inside_textfield()", &str)) { + return JSONToPoint(str, point); + } + return false; + } + + private: + void SetUpOnMainThread() override { + ContentBrowserTest::SetUpOnMainThread(); + if (!ui::TouchSelectionMenuRunner::GetInstance()) + menu_runner_.reset(new TestTouchSelectionMenuRunner); + } + + void TearDownOnMainThread() override { + menu_runner_ = nullptr; + ContentBrowserTest::TearDownOnMainThread(); + } + + scoped_ptr<TestTouchSelectionMenuRunner> menu_runner_; + + DISALLOW_COPY_AND_ASSIGN(TouchSelectionControllerClientAuraTest); +}; + +// Tests if long-pressing on a text brings up selection handles and the quick +// menu properly. +IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAuraTest, BasicSelection) { + // Set the test page up. + ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html")); + WebContents* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( + web_contents->GetRenderWidgetHostView()); + TestTouchSelectionControllerClientAura* selection_controller_client = + new TestTouchSelectionControllerClientAura(rwhva); + rwhva->SetSelectionControllerClientForTest( + make_scoped_ptr(selection_controller_client)); + + EXPECT_EQ(ui::TouchSelectionController::INACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Long-press on the text and wait for handles to appear. + selection_controller_client->InitWaitForSelectionEvent( + ui::SELECTION_HANDLES_SHOWN); + + gfx::PointF point; + ASSERT_TRUE(GetPointInsideText(&point)); + ui::GestureEvent long_press( + point.x(), point.y(), 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS)); + rwhva->OnGestureEvent(&long_press); + + selection_controller_client->Wait(); + + // Check if selection is active and the quick menu is showing. + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); +} + +// Tests if tapping in a textfield brings up the insertion handle and the quick +// menu properly. +IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAuraTest, BasicInsertion) { + // Set the test page up. + ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html")); + WebContents* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( + web_contents->GetRenderWidgetHostView()); + TestTouchSelectionControllerClientAura* selection_controller_client = + new TestTouchSelectionControllerClientAura(rwhva); + rwhva->SetSelectionControllerClientForTest( + make_scoped_ptr(selection_controller_client)); + + EXPECT_EQ(ui::TouchSelectionController::INACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Tap inside the textfield and wait for the insertion handle to appear. + selection_controller_client->InitWaitForSelectionEvent( + ui::INSERTION_HANDLE_SHOWN); + + gfx::PointF point; + ASSERT_TRUE(GetPointInsideTextfield(&point)); + ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP); + tap_details.set_tap_count(1); + ui::GestureEvent tap(point.x(), point.y(), 0, ui::EventTimeForNow(), + tap_details); + rwhva->OnGestureEvent(&tap); + + selection_controller_client->Wait(); + + // Check if insertion is active and the quick menu is showing. + EXPECT_EQ(ui::TouchSelectionController::INSERTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); +} + +// Tests if the quick menu is hidden whenever a touch point is active. +IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAuraTest, + QuickMenuHiddenOnTouch) { + // Set the test page up. + ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html")); + WebContents* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( + web_contents->GetRenderWidgetHostView()); + TestTouchSelectionControllerClientAura* selection_controller_client = + new TestTouchSelectionControllerClientAura(rwhva); + rwhva->SetSelectionControllerClientForTest( + make_scoped_ptr(selection_controller_client)); + + EXPECT_EQ(ui::TouchSelectionController::INACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Long-press on the text and wait for selection handles to appear. + selection_controller_client->InitWaitForSelectionEvent( + ui::SELECTION_HANDLES_SHOWN); + + gfx::PointF point; + ASSERT_TRUE(GetPointInsideText(&point)); + ui::GestureEvent long_press( + point.x(), point.y(), 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS)); + rwhva->OnGestureEvent(&long_press); + + selection_controller_client->Wait(); + + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + ui::test::EventGenerator generator( + web_contents->GetContentNativeView()->GetRootWindow(), + web_contents->GetContentNativeView()); + + // Put the first finger down: the quick menu should get hidden. + generator.PressTouchId(0); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Put a second finger down: the quick menu should remain hidden. + generator.PressTouchId(1); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Lift the first finger up: the quick menu should still remain hidden. + generator.ReleaseTouchId(0); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Lift the second finger up: the quick menu should re-appear. + generator.ReleaseTouchId(1); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); +} + +// Tests if the quick menu and touch handles are hidden during an scroll. +IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAuraTest, HiddenOnScroll) { + // Set the test page up. + ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html")); + WebContents* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( + web_contents->GetRenderWidgetHostView()); + TestTouchSelectionControllerClientAura* selection_controller_client = + new TestTouchSelectionControllerClientAura(rwhva); + rwhva->SetSelectionControllerClientForTest( + make_scoped_ptr(selection_controller_client)); + ui::TouchSelectionControllerTestApi selection_controller_test_api( + rwhva->selection_controller()); + + EXPECT_EQ(ui::TouchSelectionController::INACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Long-press on the text and wait for selection handles to appear. + selection_controller_client->InitWaitForSelectionEvent( + ui::SELECTION_HANDLES_SHOWN); + + gfx::PointF point; + ASSERT_TRUE(GetPointInsideText(&point)); + ui::GestureEvent long_press( + point.x(), point.y(), 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS)); + rwhva->OnGestureEvent(&long_press); + + selection_controller_client->Wait(); + + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(selection_controller_test_api.temporarily_hidden()); + EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Put a finger down: the quick menu should go away, while touch handles stay + // there. + ui::TouchEvent touch_down(ui::ET_TOUCH_PRESSED, gfx::PointF(10, 10), 0, + ui::EventTimeForNow()); + rwhva->OnTouchEvent(&touch_down); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(selection_controller_test_api.temporarily_hidden()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Start scrolling: touch handles should get hidden, while touch selection is + // still active. + ui::GestureEvent scroll_begin( + 10, 10, 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); + rwhva->OnGestureEvent(&scroll_begin); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_TRUE(selection_controller_test_api.temporarily_hidden()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // End scrolling: touch handles should re-appear. + ui::GestureEvent scroll_end( + 10, 10, 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); + rwhva->OnGestureEvent(&scroll_end); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(selection_controller_test_api.temporarily_hidden()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Lift the finger up: the quick menu should re-appear. + ui::TouchEvent touch_up(ui::ET_TOUCH_RELEASED, gfx::PointF(10, 10), 0, + ui::EventTimeForNow()); + rwhva->OnTouchEvent(&touch_up); + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(selection_controller_test_api.temporarily_hidden()); + EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); +} + +// Tests if touch selection gets deactivated after an overscroll completes. +IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAuraTest, + HiddenAfterOverscroll) { + // Set the page up. + ASSERT_NO_FATAL_FAILURE(StartTestWithPage("files/touch_selection.html")); + WebContents* web_contents = + static_cast<WebContentsImpl*>(shell()->web_contents()); + RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>( + web_contents->GetRenderWidgetHostView()); + TestTouchSelectionControllerClientAura* selection_controller_client = + new TestTouchSelectionControllerClientAura(rwhva); + rwhva->SetSelectionControllerClientForTest( + make_scoped_ptr(selection_controller_client)); + + EXPECT_EQ(ui::TouchSelectionController::INACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Long-press on the text and wait for touch handles to appear. + selection_controller_client->InitWaitForSelectionEvent( + ui::SELECTION_HANDLES_SHOWN); + + gfx::PointF point; + ASSERT_TRUE(GetPointInsideText(&point)); + ui::GestureEvent long_press( + point.x(), point.y(), 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS)); + rwhva->OnGestureEvent(&long_press); + + selection_controller_client->Wait(); + + EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); + + // Scroll such that an overscroll is initiated and wait for it to complete: + // touch selection should not be active at the end. + selection_controller_client->InitWaitForSelectionEvent( + ui::SELECTION_HANDLES_CLEARED); + + ui::GestureEvent scroll_begin( + 10, 10, 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN)); + rwhva->OnGestureEvent(&scroll_begin); + + ui::GestureEvent scroll_update( + 210, 10, 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 200, 0)); + rwhva->OnGestureEvent(&scroll_update); + + ui::GestureEvent scroll_end( + 210, 10, 0, ui::EventTimeForNow(), + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END)); + rwhva->OnGestureEvent(&scroll_end); + + selection_controller_client->Wait(); + + EXPECT_EQ(ui::TouchSelectionController::INACTIVE, + rwhva->selection_controller()->active_status()); + EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning()); +} + +} // namespace content diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 3947821..2edeacf 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -30,6 +30,8 @@ #include "content/browser/renderer_host/compositor_resize_lock_aura.h" #include "content/browser/renderer_host/dip_util.h" #include "content/browser/renderer_host/input/synthetic_gesture_target_aura.h" +#include "content/browser/renderer_host/input/touch_selection_controller_client_aura.h" +#include "content/browser/renderer_host/input/ui_touch_selection_helper.h" #include "content/browser/renderer_host/input/web_input_event_util.h" #include "content/browser/renderer_host/overscroll_controller.h" #include "content/browser/renderer_host/render_view_host_delegate.h" @@ -71,6 +73,7 @@ #include "ui/events/blink/blink_event_util.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" +#include "ui/events/gesture_detection/gesture_configuration.h" #include "ui/events/gestures/gesture_recognizer.h" #include "ui/gfx/canvas.h" #include "ui/gfx/display.h" @@ -78,6 +81,7 @@ #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/screen.h" #include "ui/gfx/skia_util.h" +#include "ui/touch_selection/touch_selection_controller.h" #include "ui/wm/public/activation_client.h" #include "ui/wm/public/scoped_tooltip_disabler.h" #include "ui/wm/public/tooltip_client.h" @@ -462,7 +466,6 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host, showing_context_menu_(false), #endif has_snapped_to_boundary_(false), - touch_editing_client_(NULL), is_guest_view_hack_(is_guest_view_hack), begin_frame_observer_proxy_(this), weak_ptr_factory_(this) { @@ -480,6 +483,10 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host, bool overscroll_enabled = base::CommandLine::ForCurrentProcess()-> GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0"; SetOverscrollControllerEnabled(overscroll_enabled); + + selection_controller_client_.reset( + new TouchSelectionControllerClientAura(this)); + CreateSelectionController(); } //////////////////////////////////////////////////////////////////////////////// @@ -965,8 +972,6 @@ void RenderWidgetHostViewAura::TextInputTypeChanged( text_input_flags_ = flags; if (GetInputMethod()) GetInputMethod()->OnTextInputTypeChanged(this); - if (touch_editing_client_) - touch_editing_client_->OnTextInputTypeChanged(text_input_type_); } } @@ -1083,11 +1088,6 @@ void RenderWidgetHostViewAura::SelectionBoundsChanged( selection_focus_ = focus_bound; if (GetInputMethod()) GetInputMethod()->OnCaretBoundsChanged(this); - - if (touch_editing_client_) { - touch_editing_client_->OnSelectionOrCursorChanged( - anchor_bound, focus_bound); - } } void RenderWidgetHostViewAura::CopyFromCompositingSurface( @@ -1178,6 +1178,10 @@ void RenderWidgetHostViewAura::OnSwapCompositorFrame( frame->metadata.device_scale_factor, frame->metadata.latency_info, &frame->metadata.satisfies_sequences); + SelectionUpdated(frame->metadata.selection.is_editable, + frame->metadata.selection.is_empty_text_form_control, + ConvertSelectionBound(frame->metadata.selection.start), + ConvertSelectionBound(frame->metadata.selection.end)); return; } @@ -1190,8 +1194,7 @@ void RenderWidgetHostViewAura::OnSwapCompositorFrame( } void RenderWidgetHostViewAura::DidStopFlinging() { - if (touch_editing_client_) - touch_editing_client_->DidStopFlinging(); + selection_controller_client_->OnScrollCompleted(); } #if defined(OS_WIN) @@ -1271,9 +1274,6 @@ void RenderWidgetHostViewAura::WheelEventAck( void RenderWidgetHostViewAura::GestureEventAck( const blink::WebGestureEvent& event, InputEventAckState ack_result) { - if (touch_editing_client_) - touch_editing_client_->GestureEventAck(event.type); - if (overscroll_controller_) { overscroll_controller_->ReceivedEventACK( event, (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result)); @@ -1675,9 +1675,8 @@ gfx::Rect RenderWidgetHostViewAura::ConvertRectFromScreen( } gfx::Rect RenderWidgetHostViewAura::GetCaretBounds() const { - gfx::Rect rect = - ui::RectBetweenSelectionBounds(selection_anchor_, selection_focus_); - return ConvertRectToScreen(rect); + return ConvertRectToScreen( + ui::RectBetweenSelectionBounds(selection_anchor_, selection_focus_)); } bool RenderWidgetHostViewAura::GetCompositionCharacterBounds( @@ -1861,8 +1860,6 @@ bool RenderWidgetHostViewAura::CanFocus() { void RenderWidgetHostViewAura::OnCaptureLost() { host_->LostCapture(); - if (touch_editing_client_) - touch_editing_client_->EndTouchEditing(false); } void RenderWidgetHostViewAura::OnPaint(const ui::PaintContext& context) { @@ -1945,8 +1942,6 @@ void RenderWidgetHostViewAura::GetHitTestMask(gfx::Path* mask) const { void RenderWidgetHostViewAura::OnKeyEvent(ui::KeyEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewAura::OnKeyEvent"); - if (touch_editing_client_ && touch_editing_client_->HandleInputEvent(event)) - return; if (popup_child_host_view_ && popup_child_host_view_->NeedsInputGrab()) { popup_child_host_view_->OnKeyEvent(event); @@ -1993,9 +1988,6 @@ void RenderWidgetHostViewAura::OnKeyEvent(ui::KeyEvent* event) { void RenderWidgetHostViewAura::OnMouseEvent(ui::MouseEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewAura::OnMouseEvent"); - if (touch_editing_client_ && touch_editing_client_->HandleInputEvent(event)) - return; - if (mouse_locked_) { aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(window_->GetRootWindow()); @@ -2159,8 +2151,6 @@ void RenderWidgetHostViewAura::OnMouseEvent(ui::MouseEvent* event) { void RenderWidgetHostViewAura::OnScrollEvent(ui::ScrollEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewAura::OnScrollEvent"); - if (touch_editing_client_ && touch_editing_client_->HandleInputEvent(event)) - return; if (event->type() == ui::ET_SCROLL) { #if !defined(OS_WIN) @@ -2189,8 +2179,8 @@ void RenderWidgetHostViewAura::OnScrollEvent(ui::ScrollEvent* event) { void RenderWidgetHostViewAura::OnTouchEvent(ui::TouchEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewAura::OnTouchEvent"); - if (touch_editing_client_ && touch_editing_client_->HandleInputEvent(event)) - return; + + bool had_no_pointer = !pointer_state_.GetPointerCount(); // Update the touch event first. if (!pointer_state_.OnTouch(*event)) { @@ -2198,10 +2188,24 @@ void RenderWidgetHostViewAura::OnTouchEvent(ui::TouchEvent* event) { return; } - blink::WebTouchEvent touch_event = ui::CreateWebTouchEventFromMotionEvent( - pointer_state_, event->may_cause_scrolling()); + blink::WebTouchEvent touch_event; + bool handled = selection_controller_->WillHandleTouchEvent(pointer_state_); + if (handled) { + event->SetHandled(); + } else { + touch_event = ui::CreateWebTouchEventFromMotionEvent( + pointer_state_, event->may_cause_scrolling()); + } pointer_state_.CleanupRemovedTouchPoints(*event); + if (handled) + return; + + if (had_no_pointer) + selection_controller_client_->OnTouchDown(); + if (!pointer_state_.GetPointerCount()) + selection_controller_client_->OnTouchUp(); + // It is important to always mark events as being handled asynchronously when // they are forwarded. This ensures that the current event does not get // processed by the gesture recognizer before events currently awaiting @@ -2216,6 +2220,7 @@ void RenderWidgetHostViewAura::OnTouchEvent(ui::TouchEvent* event) { void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) { TRACE_EVENT0("input", "RenderWidgetHostViewAura::OnGestureEvent"); + if ((event->type() == ui::ET_GESTURE_PINCH_BEGIN || event->type() == ui::ET_GESTURE_PINCH_UPDATE || event->type() == ui::ET_GESTURE_PINCH_END) && !pinch_zoom_enabled_) { @@ -2223,7 +2228,8 @@ void RenderWidgetHostViewAura::OnGestureEvent(ui::GestureEvent* event) { return; } - if (touch_editing_client_ && touch_editing_client_->HandleInputEvent(event)) + HandleGestureForTouchSelection(event); + if (event->handled()) return; // Confirm existing composition text on TAP gesture, to make sure the input @@ -2316,8 +2322,7 @@ void RenderWidgetHostViewAura::OnWindowFocused(aura::Window* gained_focus, DetachFromInputMethod(); - if (touch_editing_client_) - touch_editing_client_->EndTouchEditing(false); + selection_controller_->HideAndDisallowShowingAutomatically(); if (overscroll_controller_) overscroll_controller_->Cancel(); @@ -2374,9 +2379,8 @@ void RenderWidgetHostViewAura::OnHostMoved(const aura::WindowTreeHost* host, // RenderWidgetHostViewAura, private: RenderWidgetHostViewAura::~RenderWidgetHostViewAura() { - if (touch_editing_client_) - touch_editing_client_->OnViewDestroyed(); - + selection_controller_.reset(); + selection_controller_client_.reset(); delegated_frame_host_.reset(); window_observer_.reset(); if (window_->GetHost()) @@ -2553,6 +2557,12 @@ void RenderWidgetHostViewAura::OnShowContextMenu() { #endif } +void RenderWidgetHostViewAura::SetSelectionControllerClientForTest( + scoped_ptr<TouchSelectionControllerClientAura> client) { + selection_controller_client_.swap(client); + CreateSelectionController(); +} + void RenderWidgetHostViewAura::InternalSetBounds(const gfx::Rect& rect) { SnapToPhysicalPixelBoundary(); // Don't recursively call SetBounds if this bounds update is the result of @@ -2561,10 +2571,6 @@ void RenderWidgetHostViewAura::InternalSetBounds(const gfx::Rect& rect) { window_->SetBounds(rect); host_->WasResized(); delegated_frame_host_->WasResized(); - if (touch_editing_client_) { - touch_editing_client_->OnSelectionOrCursorChanged(selection_anchor_, - selection_focus_); - } #if defined(OS_WIN) // Create the legacy dummy window which corresponds to the bounds of the // webcontents. This will be passed as the container window for windowless @@ -2707,6 +2713,51 @@ void RenderWidgetHostViewAura::ForwardKeyboardEvent( host_->ForwardKeyboardEvent(event); } +void RenderWidgetHostViewAura::SelectionUpdated(bool is_editable, + bool is_empty_text_form_control, + const ui::SelectionBound& start, + const ui::SelectionBound& end) { + selection_controller_->OnSelectionEditable(is_editable); + selection_controller_->OnSelectionEmpty(is_empty_text_form_control); + selection_controller_->OnSelectionBoundsChanged(start, end); +} + +void RenderWidgetHostViewAura::CreateSelectionController() { + ui::TouchSelectionController::Config tsc_config; + tsc_config.tap_timeout = base::TimeDelta::FromMilliseconds( + ui::GestureConfiguration::GetInstance()->show_press_delay_in_ms()); + tsc_config.tap_slop = ui::GestureConfiguration::GetInstance() + ->max_touch_move_in_pixels_for_click(); + tsc_config.show_on_tap_for_empty_editable = true; + tsc_config.enable_longpress_drag_selection = false; + selection_controller_.reset(new ui::TouchSelectionController( + selection_controller_client_.get(), tsc_config)); +} + +void RenderWidgetHostViewAura::HandleGestureForTouchSelection( + ui::GestureEvent* event) { + switch (event->type()) { + case ui::ET_GESTURE_LONG_PRESS: + if (selection_controller_->WillHandleLongPressEvent( + base::TimeTicks() + event->time_stamp(), event->location_f())) { + event->SetHandled(); + } + break; + case ui::ET_GESTURE_TAP: + if (selection_controller_->WillHandleTapEvent(event->location_f())) + event->SetHandled(); + break; + case ui::ET_GESTURE_SCROLL_BEGIN: + selection_controller_client_->OnScrollStarted(); + break; + case ui::ET_GESTURE_SCROLL_END: + selection_controller_client_->OnScrollCompleted(); + break; + default: + break; + } +} + //////////////////////////////////////////////////////////////////////////////// // DelegatedFrameHost, public: diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index e0f76f1..b3d913f 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h @@ -31,7 +31,6 @@ #include "ui/aura/window_tree_host_observer.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/touch/selection_bound.h" -#include "ui/base/touch/touch_editing_controller.h" #include "ui/events/gestures/motion_event_aura.h" #include "ui/gfx/display_observer.h" #include "ui/gfx/geometry/insets.h" @@ -65,6 +64,7 @@ class CompositorLock; class InputMethod; class LocatedEvent; class Texture; +class TouchSelectionController; } namespace content { @@ -76,6 +76,7 @@ class OverscrollController; class RenderFrameHostImpl; class RenderWidgetHostImpl; class RenderWidgetHostView; +class TouchSelectionControllerClientAura; // RenderWidgetHostView class hierarchy described in render_widget_host_view.h. class CONTENT_EXPORT RenderWidgetHostViewAura @@ -90,49 +91,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAura public aura::client::FocusChangeObserver, public aura::client::CursorClientObserver { public: - // Displays and controls touch editing elements such as selection handles. - class TouchEditingClient { - public: - TouchEditingClient() {} - - // Tells the client to start showing touch editing handles. - virtual void StartTouchEditing() = 0; - - // Notifies the client that touch editing is no longer needed. |quick| - // determines whether the handles should fade out quickly or slowly. - virtual void EndTouchEditing(bool quick) = 0; - - // Notifies the client that the selection bounds need to be updated. - virtual void OnSelectionOrCursorChanged( - const ui::SelectionBound& anchor, - const ui::SelectionBound& focus) = 0; - - // Notifies the client that the current text input type as changed. - virtual void OnTextInputTypeChanged(ui::TextInputType type) = 0; - - // Notifies the client that an input event is about to be sent to the - // renderer. Returns true if the client wants to stop event propagation. - virtual bool HandleInputEvent(const ui::Event* event) = 0; - - // Notifies the client that a gesture event ack was received. - virtual void GestureEventAck(int gesture_event_type) = 0; - - // Notifies the client that the fling has ended, so it can activate touch - // editing if needed. - virtual void DidStopFlinging() = 0; - - // This is called when the view is destroyed, so that the client can - // perform any necessary clean-up. - virtual void OnViewDestroyed() = 0; - - protected: - virtual ~TouchEditingClient() {} - }; - - void set_touch_editing_client(TouchEditingClient* client) { - touch_editing_client_ = client; - } - // When |is_guest_view_hack| is true, this view isn't really the view for // the |widget|, a RenderWidgetHostViewGuest is. // @@ -344,6 +302,14 @@ class CONTENT_EXPORT RenderWidgetHostViewAura void SnapToPhysicalPixelBoundary(); + ui::TouchSelectionController* selection_controller() const { + return selection_controller_.get(); + } + + TouchSelectionControllerClientAura* selection_controller_client() const { + return selection_controller_client_.get(); + } + OverscrollController* overscroll_controller() const { return overscroll_controller_.get(); } @@ -351,6 +317,11 @@ class CONTENT_EXPORT RenderWidgetHostViewAura // Called when the context menu is about to be displayed. void OnShowContextMenu(); + // Used in tests to set a mock client for touch selection controller. It will + // create a new touch selection controller for the new client. + void SetSelectionControllerClientForTest( + scoped_ptr<TouchSelectionControllerClientAura> client); + protected: ~RenderWidgetHostViewAura() override; @@ -510,6 +481,20 @@ class CONTENT_EXPORT RenderWidgetHostViewAura // Called when the parent window hierarchy for our window changes. void ParentHierarchyChanged(); + // Helper function to be called whenever new selection information is + // received. It will update selection controller. + void SelectionUpdated(bool is_editable, + bool is_empty_text_form_control, + const ui::SelectionBound& start, + const ui::SelectionBound& end); + + // Helper function to create a selection controller. + void CreateSelectionController(); + + // Performs gesture handling needed for touch text selection. Sets event as + // handled if it should not be further processed. + void HandleGestureForTouchSelection(ui::GestureEvent* event); + // The model object. RenderWidgetHostImpl* host_; @@ -647,7 +632,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAura bool has_snapped_to_boundary_; - TouchEditingClient* touch_editing_client_; + scoped_ptr<TouchSelectionControllerClientAura> selection_controller_client_; + scoped_ptr<ui::TouchSelectionController> selection_controller_; scoped_ptr<OverscrollController> overscroll_controller_; |