mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			553 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			553 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2022 The Chromium Embedded Framework Authors.
 | |
| // Portions 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 "cef/libcef/browser/osr/touch_selection_controller_client_osr.h"
 | |
| 
 | |
| #include <cmath>
 | |
| #include <set>
 | |
| 
 | |
| #include "base/functional/bind.h"
 | |
| #include "cef/libcef/browser/osr/render_widget_host_view_osr.h"
 | |
| #include "cef/libcef/browser/osr/touch_handle_drawable_osr.h"
 | |
| #include "content/browser/renderer_host/render_widget_host_delegate.h"
 | |
| #include "content/browser/renderer_host/render_widget_host_impl.h"
 | |
| #include "content/public/browser/context_menu_params.h"
 | |
| #include "content/public/browser/render_view_host.h"
 | |
| #include "ui/base/clipboard/clipboard.h"
 | |
| #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 | |
| #include "ui/base/mojom/menu_source_type.mojom-forward.h"
 | |
| #include "ui/gfx/geometry/point_conversions.h"
 | |
| #include "ui/gfx/geometry/size_conversions.h"
 | |
| #include "ui/touch_selection/touch_editing_controller.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // Delay before showing the quick menu, in milliseconds.
 | |
| constexpr int kQuickMenuDelayInMs = 100;
 | |
| 
 | |
| constexpr cef_quick_menu_edit_state_flags_t kMenuCommands[] = {
 | |
|     QM_EDITFLAG_CAN_ELLIPSIS, QM_EDITFLAG_CAN_CUT, QM_EDITFLAG_CAN_COPY,
 | |
|     QM_EDITFLAG_CAN_PASTE};
 | |
| 
 | |
| constexpr int kInvalidCommandId = -1;
 | |
| constexpr cef_event_flags_t kEmptyEventFlags =
 | |
|     static_cast<cef_event_flags_t>(0);
 | |
| 
 | |
| class CefRunQuickMenuCallbackImpl : public CefRunQuickMenuCallback {
 | |
|  public:
 | |
|   using Callback = base::OnceCallback<void(int, int)>;
 | |
| 
 | |
|   explicit CefRunQuickMenuCallbackImpl(Callback callback)
 | |
|       : callback_(std::move(callback)) {}
 | |
| 
 | |
|   CefRunQuickMenuCallbackImpl(const CefRunQuickMenuCallbackImpl&) = delete;
 | |
|   CefRunQuickMenuCallbackImpl& operator=(const CefRunQuickMenuCallbackImpl&) =
 | |
|       delete;
 | |
| 
 | |
|   ~CefRunQuickMenuCallbackImpl() override {
 | |
|     if (!callback_.is_null()) {
 | |
|       // The callback is still pending. Cancel it now.
 | |
|       if (CEF_CURRENTLY_ON_UIT()) {
 | |
|         RunNow(std::move(callback_), kInvalidCommandId, kEmptyEventFlags);
 | |
|       } else {
 | |
|         CEF_POST_TASK(CEF_UIT,
 | |
|                       base::BindOnce(&CefRunQuickMenuCallbackImpl::RunNow,
 | |
|                                      std::move(callback_), kInvalidCommandId,
 | |
|                                      kEmptyEventFlags));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void Continue(int command_id, cef_event_flags_t event_flags) override {
 | |
|     if (CEF_CURRENTLY_ON_UIT()) {
 | |
|       if (!callback_.is_null()) {
 | |
|         RunNow(std::move(callback_), command_id, event_flags);
 | |
|       }
 | |
|     } else {
 | |
|       CEF_POST_TASK(CEF_UIT,
 | |
|                     base::BindOnce(&CefRunQuickMenuCallbackImpl::Continue, this,
 | |
|                                    command_id, event_flags));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void Cancel() override { Continue(kInvalidCommandId, kEmptyEventFlags); }
 | |
| 
 | |
|   void Disconnect() { callback_.Reset(); }
 | |
| 
 | |
|  private:
 | |
|   static void RunNow(Callback callback,
 | |
|                      int command_id,
 | |
|                      cef_event_flags_t event_flags) {
 | |
|     CEF_REQUIRE_UIT();
 | |
|     std::move(callback).Run(command_id, event_flags);
 | |
|   }
 | |
| 
 | |
|   Callback callback_;
 | |
| 
 | |
|   IMPLEMENT_REFCOUNTING(CefRunQuickMenuCallbackImpl);
 | |
| };
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| CefTouchSelectionControllerClientOSR::CefTouchSelectionControllerClientOSR(
 | |
|     CefRenderWidgetHostViewOSR* rwhv)
 | |
|     : rwhv_(rwhv),
 | |
|       internal_client_(rwhv),
 | |
|       active_client_(&internal_client_),
 | |
|       active_menu_client_(this),
 | |
|       quick_menu_timer_(
 | |
|           FROM_HERE,
 | |
|           base::Milliseconds(kQuickMenuDelayInMs),
 | |
|           base::BindRepeating(
 | |
|               &CefTouchSelectionControllerClientOSR::ShowQuickMenu,
 | |
|               base::Unretained(this))),
 | |
|       weak_ptr_factory_(this) {
 | |
|   DCHECK(rwhv_);
 | |
| }
 | |
| 
 | |
| CefTouchSelectionControllerClientOSR::~CefTouchSelectionControllerClientOSR() {
 | |
|   for (auto& observer : observers_) {
 | |
|     observer.OnManagerWillDestroy(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::CloseQuickMenuAndHideHandles() {
 | |
|   CloseQuickMenu();
 | |
|   rwhv_->selection_controller()->HideAndDisallowShowingAutomatically();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnWindowMoved() {
 | |
|   UpdateQuickMenu();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnTouchDown() {
 | |
|   touch_down_ = true;
 | |
|   UpdateQuickMenu();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnTouchUp() {
 | |
|   touch_down_ = false;
 | |
|   UpdateQuickMenu();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnScrollStarted() {
 | |
|   scroll_in_progress_ = true;
 | |
|   rwhv_->selection_controller()->SetTemporarilyHidden(true);
 | |
|   UpdateQuickMenu();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnScrollCompleted() {
 | |
|   scroll_in_progress_ = false;
 | |
|   active_client_->DidScroll();
 | |
|   rwhv_->selection_controller()->SetTemporarilyHidden(false);
 | |
|   UpdateQuickMenu();
 | |
| }
 | |
| 
 | |
| bool CefTouchSelectionControllerClientOSR::HandleContextMenu(
 | |
|     const content::ContextMenuParams& params) {
 | |
|   if ((params.source_type == ui::mojom::MenuSourceType::kLongPress ||
 | |
|        params.source_type == ui::mojom::MenuSourceType::kLongTap) &&
 | |
|       params.is_editable && params.selection_text.empty() &&
 | |
|       IsQuickMenuAvailable()) {
 | |
|     quick_menu_requested_ = true;
 | |
|     UpdateQuickMenu();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   const bool from_touch =
 | |
|       params.source_type == ui::mojom::MenuSourceType::kLongPress ||
 | |
|       params.source_type == ui::mojom::MenuSourceType::kLongTap ||
 | |
|       params.source_type == ui::mojom::MenuSourceType::kTouch;
 | |
|   if (from_touch && !params.selection_text.empty()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   rwhv_->selection_controller()->HideAndDisallowShowingAutomatically();
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::DidStopFlinging() {
 | |
|   OnScrollCompleted();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnSwipeToMoveCursorBegin() {
 | |
|   rwhv_->selection_controller()->OnSwipeToMoveCursorBegin();
 | |
|   OnSelectionEvent(ui::INSERTION_HANDLE_DRAG_STARTED);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnSwipeToMoveCursorEnd() {
 | |
|   rwhv_->selection_controller()->OnSwipeToMoveCursorEnd();
 | |
|   OnSelectionEvent(ui::INSERTION_HANDLE_DRAG_STOPPED);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnClientHitTestRegionUpdated(
 | |
|     ui::TouchSelectionControllerClient* client) {
 | |
|   if (client != active_client_ || !rwhv_->selection_controller() ||
 | |
|       rwhv_->selection_controller()->active_status() ==
 | |
|           ui::TouchSelectionController::INACTIVE) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   active_client_->DidScroll();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::UpdateClientSelectionBounds(
 | |
|     const gfx::SelectionBound& start,
 | |
|     const gfx::SelectionBound& end) {
 | |
|   UpdateClientSelectionBounds(start, end, &internal_client_, this);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::UpdateClientSelectionBounds(
 | |
|     const gfx::SelectionBound& start,
 | |
|     const gfx::SelectionBound& end,
 | |
|     ui::TouchSelectionControllerClient* client,
 | |
|     ui::TouchSelectionMenuClient* menu_client) {
 | |
|   if (client != active_client_ &&
 | |
|       (start.type() == gfx::SelectionBound::EMPTY || !start.visible()) &&
 | |
|       (end.type() == gfx::SelectionBound::EMPTY || !end.visible()) &&
 | |
|       (manager_selection_start_.type() != gfx::SelectionBound::EMPTY ||
 | |
|        manager_selection_end_.type() != gfx::SelectionBound::EMPTY)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   active_client_ = client;
 | |
|   active_menu_client_ = menu_client;
 | |
|   manager_selection_start_ = start;
 | |
|   manager_selection_end_ = end;
 | |
| 
 | |
|   // Notify TouchSelectionController if anything should change here. Only
 | |
|   // update if the client is different and not making a change to empty, or
 | |
|   // is the same client.
 | |
|   GetTouchSelectionController()->OnSelectionBoundsChanged(start, end);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InvalidateClient(
 | |
|     ui::TouchSelectionControllerClient* client) {
 | |
|   DCHECK(client != &internal_client_);
 | |
|   if (client == active_client_) {
 | |
|     active_client_ = &internal_client_;
 | |
|     active_menu_client_ = this;
 | |
|   }
 | |
| }
 | |
| 
 | |
| ui::TouchSelectionController*
 | |
| CefTouchSelectionControllerClientOSR::GetTouchSelectionController() {
 | |
|   return rwhv_->selection_controller();
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::AddObserver(
 | |
|     TouchSelectionControllerClientManager::Observer* observer) {
 | |
|   observers_.AddObserver(observer);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::RemoveObserver(
 | |
|     TouchSelectionControllerClientManager::Observer* observer) {
 | |
|   observers_.RemoveObserver(observer);
 | |
| }
 | |
| 
 | |
| bool CefTouchSelectionControllerClientOSR::IsQuickMenuAvailable() const {
 | |
|   DCHECK(active_menu_client_);
 | |
| 
 | |
|   const auto is_enabled = [this](cef_quick_menu_edit_state_flags_t command) {
 | |
|     return active_menu_client_->IsCommandIdEnabled(command);
 | |
|   };
 | |
|   return std::any_of(std::cbegin(kMenuCommands), std::cend(kMenuCommands),
 | |
|                      is_enabled);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::CloseQuickMenu() {
 | |
|   if (!quick_menu_running_) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   quick_menu_running_ = false;
 | |
| 
 | |
|   auto browser = rwhv_->browser_impl();
 | |
|   if (auto handler = browser->client()->GetContextMenuHandler()) {
 | |
|     handler->OnQuickMenuDismissed(browser.get(), browser->GetFocusedFrame());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::ShowQuickMenu() {
 | |
|   auto browser = rwhv_->browser_impl();
 | |
|   if (auto handler = browser->client()->GetContextMenuHandler()) {
 | |
|     gfx::RectF rect =
 | |
|         rwhv_->selection_controller()->GetVisibleRectBetweenBounds();
 | |
| 
 | |
|     gfx::PointF origin = rect.origin();
 | |
|     gfx::PointF bottom_right = rect.bottom_right();
 | |
|     auto client_bounds = gfx::RectF(rwhv_->GetViewBounds());
 | |
|     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());
 | |
| 
 | |
|     int quickmenuflags = 0;
 | |
|     for (const auto& command : kMenuCommands) {
 | |
|       if (active_menu_client_->IsCommandIdEnabled(command)) {
 | |
|         quickmenuflags |= command;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     CefRefPtr<CefRunQuickMenuCallbackImpl> callbackImpl(
 | |
|         new CefRunQuickMenuCallbackImpl(base::BindOnce(
 | |
|             &CefTouchSelectionControllerClientOSR::ExecuteCommand,
 | |
|             weak_ptr_factory_.GetWeakPtr())));
 | |
| 
 | |
|     quick_menu_running_ = true;
 | |
|     if (!handler->RunQuickMenu(
 | |
|             browser, browser->GetFocusedFrame(),
 | |
|             {static_cast<int>(std::round(origin.x())),
 | |
|              static_cast<int>(std::round(origin.y()))},
 | |
|             {static_cast<int>(std::round(size.width())),
 | |
|              static_cast<int>(std::round(size.height()))},
 | |
|             static_cast<CefContextMenuHandler::QuickMenuEditStateFlags>(
 | |
|                 quickmenuflags),
 | |
|             callbackImpl)) {
 | |
|       callbackImpl->Disconnect();
 | |
|       CloseQuickMenu();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::UpdateQuickMenu() {
 | |
|   // 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 (quick_menu_running_) {
 | |
|     CloseQuickMenu();
 | |
|   } else {
 | |
|     quick_menu_timer_.Stop();
 | |
|   }
 | |
| 
 | |
|   // Start timer to show quick menu if necessary.
 | |
|   if (ShouldShowQuickMenu()) {
 | |
|     quick_menu_timer_.Reset();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool CefTouchSelectionControllerClientOSR::SupportsAnimation() const {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool CefTouchSelectionControllerClientOSR::InternalClient::SupportsAnimation()
 | |
|     const {
 | |
|   DCHECK(false);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::SetNeedsAnimate() {
 | |
|   DCHECK(false);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InternalClient::SetNeedsAnimate() {
 | |
|   DCHECK(false);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::MoveCaret(
 | |
|     const gfx::PointF& position) {
 | |
|   active_client_->MoveCaret(position);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InternalClient::MoveCaret(
 | |
|     const gfx::PointF& position) {
 | |
|   if (auto host_delegate = rwhv_->host()->delegate()) {
 | |
|     host_delegate->MoveCaret(gfx::ToRoundedPoint(position));
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::MoveRangeSelectionExtent(
 | |
|     const gfx::PointF& extent) {
 | |
|   active_client_->MoveRangeSelectionExtent(extent);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InternalClient::
 | |
|     MoveRangeSelectionExtent(const gfx::PointF& extent) {
 | |
|   if (auto host_delegate = rwhv_->host()->delegate()) {
 | |
|     host_delegate->MoveRangeSelectionExtent(gfx::ToRoundedPoint(extent));
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::SelectBetweenCoordinates(
 | |
|     const gfx::PointF& base,
 | |
|     const gfx::PointF& extent) {
 | |
|   active_client_->SelectBetweenCoordinates(base, extent);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InternalClient::
 | |
|     SelectBetweenCoordinates(const gfx::PointF& base,
 | |
|                              const gfx::PointF& extent) {
 | |
|   if (auto host_delegate = rwhv_->host()->delegate()) {
 | |
|     host_delegate->SelectRange(gfx::ToRoundedPoint(base),
 | |
|                                gfx::ToRoundedPoint(extent));
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnSelectionEvent(
 | |
|     ui::SelectionEventType event) {
 | |
|   // This function (implicitly) uses active_menu_client_, so we don't go to the
 | |
|   // active view for this.
 | |
|   switch (event) {
 | |
|     case ui::SELECTION_HANDLES_SHOWN:
 | |
|       quick_menu_requested_ = true;
 | |
|       [[fallthrough]];
 | |
|     case ui::INSERTION_HANDLE_SHOWN:
 | |
|       UpdateQuickMenu();
 | |
|       break;
 | |
|     case ui::SELECTION_HANDLES_CLEARED:
 | |
|     case ui::INSERTION_HANDLE_CLEARED:
 | |
|       quick_menu_requested_ = false;
 | |
|       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:
 | |
|       quick_menu_requested_ = !quick_menu_requested_;
 | |
|       UpdateQuickMenu();
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InternalClient::OnSelectionEvent(
 | |
|     ui::SelectionEventType event) {
 | |
|   DCHECK(false);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::OnDragUpdate(
 | |
|     const ui::TouchSelectionDraggable::Type type,
 | |
|     const gfx::PointF& position) {}
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InternalClient::OnDragUpdate(
 | |
|     const ui::TouchSelectionDraggable::Type type,
 | |
|     const gfx::PointF& position) {
 | |
|   DCHECK(false);
 | |
| }
 | |
| 
 | |
| std::unique_ptr<ui::TouchHandleDrawable>
 | |
| CefTouchSelectionControllerClientOSR::CreateDrawable() {
 | |
|   return std::make_unique<CefTouchHandleDrawableOSR>(rwhv_);
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::DidScroll() {}
 | |
| 
 | |
| std::unique_ptr<ui::TouchHandleDrawable>
 | |
| CefTouchSelectionControllerClientOSR::InternalClient::CreateDrawable() {
 | |
|   DCHECK(false);
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::InternalClient::DidScroll() {
 | |
|   DCHECK(false);
 | |
| }
 | |
| 
 | |
| bool CefTouchSelectionControllerClientOSR::IsCommandIdEnabled(
 | |
|     int command_id) const {
 | |
|   bool editable = rwhv_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
 | |
|   bool readable = rwhv_->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD;
 | |
|   bool has_selection = !rwhv_->GetSelectedText().empty();
 | |
|   switch (command_id) {
 | |
|     case QM_EDITFLAG_CAN_ELLIPSIS:
 | |
|       return true;  // Always allowed to show the ellipsis button.
 | |
|     case QM_EDITFLAG_CAN_CUT:
 | |
|       return editable && readable && has_selection;
 | |
|     case QM_EDITFLAG_CAN_COPY:
 | |
|       return readable && has_selection;
 | |
|     case QM_EDITFLAG_CAN_PASTE: {
 | |
|       std::u16string result;
 | |
|       ui::DataTransferEndpoint data_dst = ui::DataTransferEndpoint(
 | |
|           ui::EndpointType::kDefault, {.notify_if_restricted = false});
 | |
|       ui::Clipboard::GetForCurrentThread()->ReadText(
 | |
|           ui::ClipboardBuffer::kCopyPaste, &data_dst, &result);
 | |
|       return editable && !result.empty();
 | |
|     }
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::ExecuteCommand(int command_id,
 | |
|                                                           int event_flags) {
 | |
|   if (command_id == kInvalidCommandId) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (command_id != QM_EDITFLAG_CAN_ELLIPSIS) {
 | |
|     rwhv_->selection_controller()->HideAndDisallowShowingAutomatically();
 | |
|   }
 | |
| 
 | |
|   content::RenderWidgetHostDelegate* host_delegate = rwhv_->host()->delegate();
 | |
|   if (!host_delegate) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto browser = rwhv_->browser_impl();
 | |
|   if (auto handler = browser->client()->GetContextMenuHandler()) {
 | |
|     if (handler->OnQuickMenuCommand(
 | |
|             browser.get(), browser->GetFocusedFrame(), command_id,
 | |
|             static_cast<cef_event_flags_t>(event_flags))) {
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   switch (command_id) {
 | |
|     case QM_EDITFLAG_CAN_CUT:
 | |
|       host_delegate->Cut();
 | |
|       break;
 | |
|     case QM_EDITFLAG_CAN_COPY:
 | |
|       host_delegate->Copy();
 | |
|       break;
 | |
|     case QM_EDITFLAG_CAN_PASTE:
 | |
|       host_delegate->Paste();
 | |
|       break;
 | |
|     case QM_EDITFLAG_CAN_ELLIPSIS:
 | |
|       CloseQuickMenu();
 | |
|       RunContextMenu();
 | |
|       break;
 | |
|     default:
 | |
|       // Invalid command, do nothing.
 | |
|       // Also reached when callback is destroyed/cancelled.
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefTouchSelectionControllerClientOSR::RunContextMenu() {
 | |
|   const gfx::RectF anchor_rect =
 | |
|       rwhv_->selection_controller()->GetVisibleRectBetweenBounds();
 | |
|   const gfx::PointF anchor_point =
 | |
|       gfx::PointF(anchor_rect.CenterPoint().x(), anchor_rect.y());
 | |
|   rwhv_->host()->ShowContextMenuAtPoint(
 | |
|       gfx::ToRoundedPoint(anchor_point),
 | |
|       ui::mojom::MenuSourceType::kTouchEditMenu);
 | |
| 
 | |
|   // Hide selection handles after getting rect-between-bounds from touch
 | |
|   // selection controller; otherwise, rect would be empty and the above
 | |
|   // calculations would be invalid.
 | |
|   rwhv_->selection_controller()->HideAndDisallowShowingAutomatically();
 | |
| }
 | |
| 
 | |
| bool CefTouchSelectionControllerClientOSR::ShouldShowQuickMenu() {
 | |
|   return quick_menu_requested_ && !touch_down_ && !scroll_in_progress_ &&
 | |
|          !handle_drag_in_progress_ && IsQuickMenuAvailable();
 | |
| }
 | |
| 
 | |
| std::u16string CefTouchSelectionControllerClientOSR::GetSelectedText() {
 | |
|   return rwhv_->GetSelectedText();
 | |
| }
 |