[M108 Migration] Segregate ContextMenuControllerEfl into a base class 36/289136/6
authorAyush Kumar <ayush.k123@samsung.com>
Mon, 27 Feb 2023 08:34:15 +0000 (14:04 +0530)
committerBot Blink <blinkbot@samsung.com>
Wed, 8 Mar 2023 11:10:37 +0000 (11:10 +0000)
1. Split ContextMenuControllerEfl into two parts, one of which
will be recycled by WRTJS as well
2. Removed redundant and extra headers and cleaned up the code
a bit
3. Modified/Removed functions from ContextMenuController which
are no longer needed in m108

Reference:
1. https://review.tizen.org/gerrit/282571
2. https://review.tizen.org/gerrit/284343

Change-Id: Ifc9730734bea5a0bf530b553bd00a90e0f39f136
Signed-off-by: Ayush Kumar <ayush.k123@samsung.com>
content/browser/web_contents/web_contents_view_aura.h
tizen_src/chromium_impl/content/browser/browser_efl.gni
tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/BUILD.gn
tizen_src/ewk/efl_integration/context_menu_controller_efl.cc
tizen_src/ewk/efl_integration/context_menu_controller_efl.h
tizen_src/ewk/efl_integration/private/ewk_context_menu_item_private.h [new file with mode: 0644]
tizen_src/ewk/efl_integration/private/ewk_context_menu_private.h

index db0945e..7e4abc8 100644 (file)
@@ -86,6 +86,10 @@ class CONTENT_EXPORT WebContentsViewAura
   }
 #endif
 
+#if BUILDFLAG(IS_TIZEN)
+  int GetOrientation() { return orientation_; }
+#endif
+
  private:
   // Just the metadata from DropTargetEvent that's safe and cheap to copy to
   // help locate drop events in the callback.
@@ -412,6 +416,10 @@ class CONTENT_EXPORT WebContentsViewAura
   std::unique_ptr<WebContentsViewAuraHelperEfl> wcva_helper_;
 #endif
 
+#if BUILDFLAG(IS_TIZEN)
+  int orientation_ = 0;
+#endif
+
   base::WeakPtrFactory<WebContentsViewAura> weak_ptr_factory_{this};
 };
 
index 0830e10..03a1545 100644 (file)
@@ -75,6 +75,8 @@ if (enable_wrt_js) {
 # Source
 ##############################################################################
 external_content_browser_efl_sources = [
+  "//tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.cc",
+  "//tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.h",
   "//tizen_src/chromium_impl/content/browser/date_time_chooser_efl.cc",
   "//tizen_src/chromium_impl/content/browser/date_time_chooser_efl.h",
   "//tizen_src/chromium_impl/content/browser/input_picker/input_picker_base.cc",
diff --git a/tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.cc b/tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.cc
new file mode 100644 (file)
index 0000000..a740b9c
--- /dev/null
@@ -0,0 +1,641 @@
+// Copyright 2022 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "context_menu_controller_base.h"
+
+#include <Elementary.h>
+
+#include "content/browser/renderer_host/render_widget_host_view_aura.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_contents/web_contents_view_aura.h"
+#include "tizen_src/chromium_impl/tizen/system_info.h"
+#include "tizen_src/ewk/efl_integration/private/ewk_context_menu_item_private.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/dip_util.h"
+#include "ui/gfx/geometry/rect.h"
+
+#if BUILDFLAG(IS_TIZEN)
+#include <app_control.h>
+#include <efl_extension.h>
+#endif
+
+namespace content {
+
+namespace {
+// The height of the selection menu.
+static const int kMenuHeight = 64;
+
+// Popup will be restored after kRestoreTime if it is dismissed unexpectedly.
+static const float kRestoreTime = 0.5f;
+
+const int kMinHeightVertical = 840;
+const int kMinHeightHorizontal = 420;
+}  // namespace
+
+int ContextMenuControllerBase::_popup_item_height = 96;
+std::vector<ContextMenuItemEfl>
+    ContextMenuControllerBase::_context_menu_listdata;
+
+ContextMenuControllerBase::ContextMenuControllerBase(WebContents& web_contents)
+    : web_contents_(web_contents) {
+  native_view_ =
+      static_cast<WebContentsImpl*>(&web_contents_)->GetEflNativeView();
+}
+
+ContextMenuControllerBase::~ContextMenuControllerBase() {
+  _context_menu_listdata.clear();
+  DeletePopup();
+}
+
+void ContextMenuControllerBase::Move(int x, int y) {
+  if (popup_ && is_text_selection_)
+    evas_object_move(popup_, x, y);
+}
+
+bool ContextMenuControllerBase::CreateContextMenu(
+    const ContextMenuParams& params) {
+  is_text_selection_ = false;
+  Eina_List* ittr;
+  void* data;
+  EINA_LIST_FOREACH(menu_items_, ittr, data) {
+    _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*>(data);
+    ContextMenuItemEfl* context_item = item->GetMenuItem();
+    switch (context_item->GetContextMenuOption()) {
+      case EWK_CONTEXT_MENU_ITEM_TAG_COPY:
+      case EWK_CONTEXT_MENU_ITEM_TAG_SELECT_ALL:
+      case EWK_CONTEXT_MENU_ITEM_TAG_SELECT_WORD:
+      case EWK_CONTEXT_MENU_ITEM_TAG_PASTE:
+        is_text_selection_ = true;
+        break;
+      default:
+        break;
+    }
+  }
+
+  if (is_text_selection_) {
+    popup_ = elm_ctxpopup_add(native_view_);
+#if BUILDFLAG(IS_TIZEN)
+    elm_ctxpopup_horizontal_set(popup_, EINA_TRUE);
+#endif
+    elm_object_tree_focus_allow_set(popup_, false);
+  } else {
+    top_widget_ =
+        elm_object_top_widget_get(elm_object_parent_widget_get(evas_object()));
+    evas_object_data_set(top_widget_, "ContextMenuContollerEfl", this);
+    popup_ = elm_popup_add(top_widget_);
+
+    if (IsMobileProfile())
+      elm_popup_align_set(popup_, ELM_NOTIFY_ALIGN_FILL, 1.0);
+    else
+      elm_popup_align_set(popup_, 0.5, 0.5);
+  }
+
+  if (!popup_)
+    return false;
+
+  if (!IsMobileProfile())
+    evas_object_smart_member_add(popup_, evas_object());
+
+  elm_object_tree_focus_allow_set(popup_, EINA_FALSE);
+
+  evas_object_data_set(popup_, "ContextMenuContollerEfl", this);
+
+  _context_menu_listdata.clear();
+  if (is_text_selection_) {
+    EINA_LIST_FOREACH(menu_items_, ittr, data) {
+      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*>(data);
+      ContextMenuItemEfl* context_item = item->GetMenuItem();
+      Elm_Object_Item* appended_item = 0;
+
+      if (!context_item->Title().empty()) {
+        appended_item = elm_ctxpopup_item_append(
+            popup_, context_item->Title().c_str(), 0,
+            ContextMenuItemSelectedCallback, context_item);
+      } else {
+        appended_item = elm_ctxpopup_item_append(
+            popup_, 0, 0, ContextMenuItemSelectedCallback, context_item);
+      }
+
+      if (appended_item)
+        _context_menu_listdata.push_back(*context_item);
+    }
+#if BUILDFLAG(IS_TIZEN)
+    // Workaround
+    // Need to set "copypaste" style, to let moving handles
+    // when ctxpopup is visible
+    // http://107.108.218.239/bugzilla/show_bug.cgi?id=11613
+    elm_object_style_set(popup_, "copypaste");
+#endif
+
+    evas_object_size_hint_weight_set(popup_, EVAS_HINT_EXPAND,
+                                     EVAS_HINT_EXPAND);
+    evas_object_size_hint_max_set(popup_, -1, _popup_item_height);
+    evas_object_size_hint_min_set(popup_, 0, _popup_item_height);
+  } else {
+    std::string selected_link(params_.link_url.spec());
+    if (!selected_link.empty()) {
+      if (base::StartsWith(selected_link, std::string("mailto:"),
+                           base::CompareCase::INSENSITIVE_ASCII) ||
+          base::StartsWith(selected_link, std::string("tel:"),
+                           base::CompareCase::INSENSITIVE_ASCII)) {
+        size_t current_pos = 0;
+        size_t next_delimiter = 0;
+        // Remove the First part and Extract actual URL or title
+        // (mailto:abc@d.com?id=345  ==> abc@d.com?id=345)
+        if ((next_delimiter = selected_link.find(":", current_pos)) !=
+            std::string::npos) {
+          current_pos = next_delimiter + 1;
+        }
+
+        std::string last_part = selected_link.substr(current_pos);
+        std::string core_part = selected_link.substr(current_pos);
+        current_pos = 0;
+        next_delimiter = 0;
+        // Trim the core by removing ? at the end (abc@d.com?id=345  ==>
+        // abc@d.com)
+        if ((next_delimiter = core_part.find("?", current_pos)) !=
+            std::string::npos) {
+          last_part =
+              core_part.substr(current_pos, next_delimiter - current_pos);
+        }
+      } else {
+        elm_object_part_text_set(popup_, "title,text", selected_link.c_str());
+      }
+    } else {
+      std::string selected_image(params_.src_url.spec());
+      if (!selected_image.empty())
+        elm_object_part_text_set(popup_, "title,text", selected_image.c_str());
+    }
+
+    evas_object_size_hint_weight_set(popup_, EVAS_HINT_EXPAND,
+                                     EVAS_HINT_EXPAND);
+
+    list_ = elm_list_add(popup_);
+    elm_list_mode_set(list_, ELM_LIST_EXPAND);
+    evas_object_data_set(list_, "ContextMenuContollerEfl", this);
+
+    Eina_List* ittr;
+    void* data;
+    EINA_LIST_FOREACH(menu_items_, ittr, data) {
+      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*>(data);
+      ContextMenuItemEfl* context_item = item->GetMenuItem();
+      if (!context_item->Title().empty()) {
+        Elm_Object_Item* appended_item = elm_list_item_append(
+            list_, context_item->Title().c_str(), NULL, NULL,
+            ContextMenuItemSelectedCallback, context_item);
+
+        if (appended_item)
+          _context_menu_listdata.push_back(*context_item);
+      }
+    }
+
+    ContextMenuPopupListResize();
+
+    evas_object_show(list_);
+    elm_object_content_set(popup_, list_);
+    evas_object_event_callback_add(popup_, EVAS_CALLBACK_RESIZE,
+                                   ContextMenuPopupResize, this);
+  }
+
+  if (_context_menu_listdata.empty()) {
+    DeletePopup();
+    return false;
+  }
+  return true;
+}
+
+// static
+void ContextMenuControllerBase::ContextMenuHWBackKey(void* data,
+                                                     Evas_Object* obj,
+                                                     void* event_info) {
+  auto menu_controller = static_cast<ContextMenuControllerBase*>(data);
+  if (menu_controller) {
+    auto selection_controller = menu_controller->GetSelectionController();
+    if (selection_controller)
+      selection_controller->ToggleCaretAfterSelection();
+
+    menu_controller->HideContextMenu();
+    evas_object_data_del(obj, "ContextEfl");
+  }
+}
+
+// static
+Eina_Bool ContextMenuControllerBase::RestoreTimerCallback(void* data) {
+  auto* menu_controller = static_cast<ContextMenuControllerBase*>(data);
+  if (!menu_controller)
+    return ECORE_CALLBACK_CANCEL;
+
+  auto selection_controller = menu_controller->GetSelectionController();
+
+  if (menu_controller->popup_ && selection_controller &&
+      selection_controller->GetSelectionStatus() &&
+      !selection_controller->IsCaretMode()) {
+    evas_object_show(menu_controller->popup_);
+  }
+
+  menu_controller->restore_timer_ = nullptr;
+
+  return ECORE_CALLBACK_CANCEL;
+}
+
+// static
+void ContextMenuControllerBase::ContextMenuCancelCallback(void* data,
+                                                          Evas_Object* obj,
+                                                          void* event_info) {
+  ContextMenuControllerBase* menu_controller =
+      static_cast<ContextMenuControllerBase*>(
+          evas_object_data_get(obj, "ContextMenuContollerEfl"));
+  if (!menu_controller)
+    return;
+
+  RenderWidgetHostViewAura* rwhva = menu_controller->rwhva();
+  if (!rwhva)
+    return;
+
+  // context_menu is dismissed when we touch outside of popup.
+  // It is policy of elm_ctxpopup by EFL team.
+  // But according to wcs TC(21~23) context_menu should be displayed,
+  // when touchevent is consumed. So we should show context_menu again when
+  // touchevent is consumed.
+  if (rwhva->offscreen_helper()->IsTouchstartConsumed() ||
+      rwhva->offscreen_helper()->IsTouchendConsumed()) {
+    evas_object_show(menu_controller->popup_);
+    return;
+  }
+
+  // TODO: If |elm_ctxpopup_auto_hide_disabled_set| is works properly,
+  // We should remove this timer.
+  // Bug: http://suprem.sec.samsung.net/jira/browse/TNEXT-67
+  menu_controller->restore_timer_ =
+      ecore_timer_add(kRestoreTime, RestoreTimerCallback, menu_controller);
+}
+
+// static
+void ContextMenuControllerBase::ContextMenuItemSelectedCallback(
+    void* data,
+    Evas_Object* obj,
+    void* event_info) {
+  Evas_Object* pop_up = obj;
+  ContextMenuControllerBase* menu_controller =
+      static_cast<ContextMenuControllerBase*>(
+          evas_object_data_get(pop_up, "ContextMenuContollerEfl"));
+
+  if (menu_controller) {
+    ContextMenuItemEfl* selected_menu_item =
+        static_cast<ContextMenuItemEfl*>(data);
+    if (selected_menu_item)
+      menu_controller->MenuItemSelected(selected_menu_item);
+  }
+}
+
+void ContextMenuControllerBase::RequestSelectionRect() const {
+  CHECK(rwhva());
+  rwhva()->host()->RequestSelectionRect();
+}
+
+gfx::Point ContextMenuControllerBase::CalculateSelectionMenuPosition(
+    const gfx::Rect& selection_rect) {
+  auto* selection_controller = GetSelectionController();
+  if (!selection_controller)
+    return gfx::Point();
+
+  auto visible_viewport_rect = selection_controller->GetVisibleViewportRect();
+  auto forbidden_rect =
+      selection_controller->GetForbiddenRegionRect(selection_rect);
+
+  gfx::Point position;
+  // Selection menu should always be placed at the centre of visible selection
+  // area horizontally.
+  position.set_x(forbidden_rect.CenterPoint().x());
+  // If possible, selection menu should always be placed above forbidden
+  // region.
+
+  if (forbidden_rect.y() > visible_viewport_rect.y() + kMenuHeight) {
+    // If possible, selection menu should always be placed above forbidden
+    // region.
+    position.set_y(forbidden_rect.y());
+  } else if (forbidden_rect.bottom() + kMenuHeight <
+             visible_viewport_rect.bottom()) {
+    // If there is no sufficient space above, we're trying to place selection
+    // menu below forbidden region.
+    position.set_y(forbidden_rect.bottom() + kMenuHeight);
+  } else {
+    // If there is no sufficient space above and below, selection menu will be
+    // shown at the centre of visible selection area vertically.
+    position.set_y(forbidden_rect.CenterPoint().y());
+  }
+
+  return position;
+}
+
+void ContextMenuControllerBase::OnSelectionRectReceived(
+    gfx::Rect selection_rect) {
+  auto controller = GetSelectionController();
+
+  if (IsMobileProfile() && (context_menu_status_ != INPROGRESS))
+    return;
+
+  if (!popup_ || !rwhva() || !controller)
+    return;
+
+  float device_scale =
+      display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
+  gfx::Rect selection_rect_dip = gfx::ToEnclosingRect(
+      ConvertRectToPixels(gfx::Rect(selection_rect), device_scale));
+
+  // Consider a webview offset.
+  selection_rect_dip.Offset(
+      rwhva()->offscreen_helper()->GetViewBoundsInPix().OffsetFromOrigin());
+
+  auto visible_viewport_rect = controller->GetVisibleViewportRect();
+
+  // In case of the caret mode selection_rect is empty
+  if (!controller->IsCaretMode() &&
+      gfx::IntersectRects(visible_viewport_rect, selection_rect_dip)
+          .IsEmpty()) {
+    return;
+  }
+
+  gfx::Point selection_menu_position =
+      CalculateSelectionMenuPosition(selection_rect_dip);
+  if (!visible_viewport_rect.Contains(selection_menu_position))
+    return;
+
+  evas_object_smart_callback_add(popup_, "dismissed", ContextMenuCancelCallback,
+                                 this);
+
+  if (IsMobileProfile())
+    elm_ctxpopup_auto_hide_disabled_set(popup_, EINA_TRUE);
+
+#if BUILDFLAG(IS_TIZEN)
+  eext_object_event_callback_add(popup_, EEXT_CALLBACK_BACK,
+                                 ContextMenuHWBackKey, this);
+#endif
+  evas_object_move(popup_, selection_menu_position.x(),
+                   selection_menu_position.y());
+
+  controller->ContextMenuStatusVisible();
+
+  evas_object_show(popup_);
+  evas_object_raise(popup_);
+  if (IsMobileProfile())
+    context_menu_status_ = VISIBLE;
+}
+
+bool ContextMenuControllerBase::ShowContextMenu() {
+  if (!popup_)
+    return false;
+
+  if (IsMobileProfile())
+    context_menu_status_ = INPROGRESS;
+
+  if (is_text_selection_) {
+    // Selection menu will be shown in OnSelectionRectReceived after receiving
+    // selection rectangle form the renderer.
+    RequestSelectionRect();
+  } else {
+    evas_object_smart_callback_add(popup_, "block,clicked",
+                                   BlockClickedCallback, this);
+#if BUILDFLAG(IS_TIZEN)
+    eext_object_event_callback_add(popup_, EEXT_CALLBACK_BACK,
+                                   ContextMenuHWBackKey, this);
+    eext_object_event_callback_add(popup_, EEXT_CALLBACK_MORE,
+                                   ContextMenuHWBackKey, this);
+#endif
+    evas_object_show(popup_);
+    evas_object_raise(popup_);
+    if (IsMobileProfile())
+      context_menu_status_ = VISIBLE;
+  }
+  return true;
+}
+
+void ContextMenuControllerBase::HideSelectionHandle() {
+  auto* controller = GetSelectionController();
+  if (controller)
+    controller->HideHandles();
+}
+
+void ContextMenuControllerBase::ClearSelection() {
+  auto controller = GetSelectionController();
+  if (controller)
+    controller->ClearSelection();
+}
+
+void ContextMenuControllerBase::RequestShowSelectionHandleAndContextMenu(
+    bool trigger_selection_change) {
+  auto* controller = GetSelectionController();
+  if (controller) {
+    controller->SetSelectionStatus(true);
+    controller->SetWaitsForRendererSelectionChanges(
+        SelectionControllerEfl::Reason::RequestedByContextMenu);
+    if (trigger_selection_change)
+      controller->TriggerOnSelectionChange();
+  }
+}
+
+#if BUILDFLAG(IS_TIZEN)
+static void destroy_app_handle(app_control_h* handle) {
+  std::ignore = app_control_destroy(*handle);
+}
+
+void ContextMenuControllerBase::LaunchShareApp(const std::string& text) {
+  app_control_h handle = nullptr;
+  int error_code = app_control_create(&handle);
+  if (error_code < 0 || !handle) {
+    LOG(ERROR) << __PRETTY_FUNCTION__
+               << " : Failed to create share service. Error code: "
+               << error_code;
+    return;
+  }
+
+  std::unique_ptr<app_control_h, decltype(&destroy_app_handle)> handle_ptr(
+      &handle, destroy_app_handle);
+
+  error_code =
+      app_control_set_operation(handle, APP_CONTROL_OPERATION_SHARE_TEXT);
+  if (error_code < 0) {
+    LOG(ERROR) << __PRETTY_FUNCTION__
+               << ": Failed to set share text operation. Error code: "
+               << error_code;
+    return;
+  }
+
+  error_code =
+      app_control_add_extra_data(handle, APP_CONTROL_DATA_TEXT, text.c_str());
+  if (error_code < 0) {
+    LOG(ERROR) << __PRETTY_FUNCTION__
+               << ": Failed to add data for share application. Error code: "
+               << error_code;
+    return;
+  }
+
+  error_code = app_control_send_launch_request(handle, nullptr, nullptr);
+  if (error_code < 0) {
+    LOG(ERROR) << __PRETTY_FUNCTION__
+               << ": Failed to send launch request for share application. "
+               << "Error code: " << error_code;
+    return;
+  }
+}
+#endif
+
+// static
+void ContextMenuControllerBase::ContextMenuPopupResize(void* data,
+                                                       Evas* e,
+                                                       Evas_Object* obj,
+                                                       void* event_info) {
+  auto* thiz = static_cast<ContextMenuControllerBase*>(data);
+  if (thiz)
+    thiz->ContextMenuPopupListResize();
+}
+
+// static
+void ContextMenuControllerBase::BlockClickedCallback(void* data,
+                                                     Evas_Object*,
+                                                     void*) {
+  ContextMenuControllerBase* menu_controller =
+      static_cast<ContextMenuControllerBase*>(data);
+  if (!menu_controller)
+    return;
+
+  menu_controller->HideContextMenu();
+}
+
+void ContextMenuControllerBase::ContextMenuPopupListResize() {
+  if (!IsMobileProfile())
+    return;
+
+  int orientation = GetOrientation();
+  if ((orientation == 90 || orientation == 270) &&
+      _context_menu_listdata.size() > 3)
+    evas_object_size_hint_min_set(list_, 0, kMinHeightHorizontal);
+  else if ((orientation == 0 || orientation == 180) &&
+           _context_menu_listdata.size() > 7)
+    evas_object_size_hint_min_set(list_, 0, kMinHeightVertical);
+  else
+    evas_object_size_hint_min_set(
+        list_, 0, _context_menu_listdata.size() * _popup_item_height);
+
+  evas_object_show(list_);
+}
+
+void ContextMenuControllerBase::HideContextMenu() {
+  if (IsMobileProfile() &&
+      (context_menu_status_ == HIDDEN || context_menu_status_ == NONE))
+    return;
+
+  if (is_text_selection_) {
+    SelectionControllerEfl* controller = GetSelectionController();
+    if (controller)
+      controller->ContextMenuStatusHidden();
+  }
+
+  if (popup_) {
+    if (IsMobileProfile()) {
+      evas_object_hide(popup_);
+    } else {
+      evas_object_event_callback_del(popup_, EVAS_CALLBACK_RESIZE,
+                                     ContextMenuPopupResize);
+      evas_object_del(popup_);
+      popup_ = nullptr;
+    }
+  }
+
+  if (restore_timer_) {
+    ecore_timer_del(restore_timer_);
+    restore_timer_ = nullptr;
+  }
+
+  if (IsMobileProfile()) {
+    context_menu_status_ = HIDDEN;
+  } else if (menu_items_) {
+    void* data;
+    EINA_LIST_FREE(menu_items_, data) {
+      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*>(data);
+      delete item;
+    }
+    menu_items_ = nullptr;
+  }
+}
+
+void ContextMenuControllerBase::Resize(const gfx::Rect& webview_rect) {
+  if (!popup_)
+    return;
+
+  if (IsMobileProfile() && context_menu_status_ != VISIBLE)
+    return;
+
+  if (is_text_selection_) {
+    RequestSelectionRect();
+  } else {
+    evas_object_resize(popup_, webview_rect.width(), webview_rect.height());
+  }
+}
+
+void ContextMenuControllerBase::DeletePopup() {
+  if (top_widget_)
+    evas_object_data_set(top_widget_, "ContextMenuContollerEfl", 0);
+
+  if (is_text_selection_) {
+    SelectionControllerEfl* controller = GetSelectionController();
+    if (controller)
+      controller->ContextMenuStatusHidden();
+  }
+
+  if (popup_) {
+    evas_object_data_set(popup_, "ContextMenuContollerEfl", 0);
+    if (!IsMobileProfile())
+      evas_object_smart_member_del(popup_);
+    evas_object_del(popup_);
+    popup_ = nullptr;
+    list_ = nullptr;
+  }
+
+  if (menu_items_) {
+    void* data;
+    EINA_LIST_FREE(menu_items_, data) {
+      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*>(data);
+      delete item;
+    }
+    menu_items_ = nullptr;
+  }
+  if (IsMobileProfile())
+    context_menu_status_ = NONE;
+}
+
+RenderWidgetHostViewAura* ContextMenuControllerBase::rwhva() const {
+  return static_cast<RenderWidgetHostViewAura*>(
+      web_contents_.GetRenderWidgetHostView());
+}
+
+WebContentsViewAura* ContextMenuControllerBase::GetWebContentsViewAura() const {
+  WebContentsImpl* wc = static_cast<WebContentsImpl*>(&web_contents_);
+  return static_cast<WebContentsViewAura*>(wc->GetView());
+}
+
+int ContextMenuControllerBase::GetOrientation() {
+#if BUILDFLAG(IS_TIZEN)
+  return GetWebContentsViewAura()->GetOrientation();
+#else
+  return 0;
+#endif
+}
+
+SelectionControllerEfl* ContextMenuControllerBase::GetSelectionController() {
+  RenderViewHost* render_view_host = web_contents_.GetRenderViewHost();
+  RenderWidgetHostViewAura* view = static_cast<RenderWidgetHostViewAura*>(
+      render_view_host->GetWidget()->GetView());
+  return view ? view->offscreen_helper()->GetSelectionController() : 0;
+}
+
+const char* ContextMenuControllerBase::GetSelectedText() {
+  if (!rwhva())
+    return "";
+
+  std::string selected_text = base::UTF16ToUTF8(rwhva()->GetSelectedText());
+  return selected_text.c_str();
+}
+
+}  // namespace content
diff --git a/tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.h b/tizen_src/chromium_impl/content/browser/context_menu/context_menu_controller_base.h
new file mode 100644 (file)
index 0000000..b8d427e
--- /dev/null
@@ -0,0 +1,145 @@
+// Copyright 2022 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef context_menu_controller_base_h
+#define context_menu_controller_base_h
+
+#include <Ecore.h>
+#include <Evas.h>
+
+#include "content/public/browser/context_menu_params.h"
+#include "content/public/browser/web_contents.h"
+#include "tizen_src/ewk/efl_integration/public/ewk_context_menu_internal.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace content {
+class RenderWidgetHostViewAura;
+class SelectionControllerEfl;
+class WebContents;
+class WebContentsViewAura;
+
+class ContextMenuItemEfl {
+ public:
+  ContextMenuItemEfl(Ewk_Context_Menu_Item_Type item,
+                     Ewk_Context_Menu_Item_Tag option,
+                     const std::string& title,
+                     const std::string& image_url = std::string(),
+                     const std::string& link_url = std::string(),
+                     const std::string& icon_path = std::string())
+      : menu_type_(item),
+        menu_option_(option),
+        title_(title),
+        is_enabled_(true),
+        image_url_(image_url),
+        link_url_(link_url),
+        icon_path_(icon_path) {}
+
+  ~ContextMenuItemEfl() {}
+
+  const std::string& Title() const { return title_; }
+  void SetTitle(const std::string& title) { title_ = title; }
+
+  bool IsEnabled() const { return is_enabled_; }
+  void SetEnabled(bool status) { is_enabled_ = status; }
+  Ewk_Context_Menu_Item_Tag GetContextMenuOption() const {
+    return menu_option_;
+  }
+  Ewk_Context_Menu_Item_Type GetContextMenuOptionType() const {
+    return menu_type_;
+  }
+  const std::string& LinkURL() const { return link_url_; }
+  const std::string& ImageURL() const { return image_url_; }
+  const std::string& IconPath() const { return icon_path_; }
+
+ private:
+  Ewk_Context_Menu_Item_Type menu_type_;
+  Ewk_Context_Menu_Item_Tag menu_option_;
+  std::string title_;
+  bool is_enabled_;
+  std::string image_url_;
+  std::string link_url_;
+  std::string icon_path_;
+};
+
+class ContextMenuControllerBase {
+ public:
+  ContextMenuControllerBase(WebContents& web_contents);
+  virtual ~ContextMenuControllerBase();
+
+  ContextMenuControllerBase(const ContextMenuControllerBase&) = delete;
+  ContextMenuControllerBase& operator=(const ContextMenuControllerBase&) =
+      delete;
+
+  static void ContextMenuCancelCallback(void* data,
+                                        Evas_Object* obj,
+                                        void* event_info);
+  static void ContextMenuItemSelectedCallback(void* data,
+                                              Evas_Object* obj,
+                                              void* event_info);
+  static void ContextMenuHWBackKey(void* data,
+                                   Evas_Object* obj,
+                                   void* event_info);
+
+  void Move(int x, int y);
+  void HideContextMenu();
+
+  void RequestSelectionRect() const;
+  void OnSelectionRectReceived(gfx::Rect selection_rect);
+  gfx::Point CalculateSelectionMenuPosition(const gfx::Rect& selection_rect);
+
+  virtual void Resize(const gfx::Rect& webview_rect);
+  virtual void MenuItemSelected(ContextMenuItemEfl* menu_item) = 0;
+
+ protected:
+  bool CreateContextMenu(const ContextMenuParams& params);
+  bool ShowContextMenu();
+  void HideSelectionHandle();
+  void ClearSelection();
+  void RequestShowSelectionHandleAndContextMenu(
+      bool trigger_selection_change = false);
+#if BUILDFLAG(IS_TIZEN)
+  void LaunchShareApp(const std::string& text);
+#endif
+  RenderWidgetHostViewAura* rwhva() const;
+  SelectionControllerEfl* GetSelectionController();
+
+  Eina_List* menu_items_ = nullptr;
+  ContextMenuParams params_;
+  WebContents& web_contents_;
+  bool is_text_selection_ = false;
+
+ private:
+  virtual Evas_Object* evas_object() = 0;
+
+  enum ContextMenuStatusTag { NONE = 0, HIDDEN, INPROGRESS, VISIBLE };
+  static void ContextMenuPopupResize(void* data,
+                                     Evas* e,
+                                     Evas_Object* obj,
+                                     void* event_info);
+  static void BlockClickedCallback(void*, Evas_Object*, void*);
+  void ContextMenuPopupListResize();
+  static Eina_Bool RestoreTimerCallback(void* data);
+
+  void DeletePopup();
+  int GetOrientation();
+  virtual const char* GetSelectedText();
+  WebContentsViewAura* GetWebContentsViewAura() const;
+
+  static std::vector<ContextMenuItemEfl> _context_menu_listdata;
+  static int _popup_item_height;
+
+  Evas_Object* native_view_;
+  Evas_Object* top_widget_ = nullptr;
+  Evas_Object* popup_ = nullptr;
+  Evas_Object* list_ = nullptr;
+  Ecore_Timer* restore_timer_ = nullptr;
+  ContextMenuStatusTag context_menu_status_ = NONE;
+};
+
+}  // namespace content
+
+#endif  // context_menu_controller_base_h
index eacbd7d..33fac56 100755 (executable)
@@ -392,6 +392,7 @@ shared_library("chromium-ewk") {
     "private/ewk_console_message_private.h",
     "private/ewk_context_form_autofill_profile_private.cc",
     "private/ewk_context_form_autofill_profile_private.h",
+    "private/ewk_context_menu_item_private.h",
     "private/ewk_context_menu_private.h",
     "private/ewk_context_private.cc",
     "private/ewk_context_private.h",
index 838a328..b8e0ae2 100644 (file)
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <Elementary.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include "context_menu_controller_efl.h"
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/strings/escape.h"
 #include "base/strings/utf_string_conversions.h"
 #include "browser_context_efl.h"
-#include "common/web_contents_utils.h"
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
 #include "content/browser/selection/selection_controller_efl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/paths_efl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_manager.h"
-#include "content/public/browser/navigation_entry.h"
-#include "content/public/browser/render_process_host.h"
-#include "context_menu_controller_efl.h"
+#include "content/public/browser/render_view_host.h"
 #include "eweb_view.h"
 #include "net/base/filename_util.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "private/ewk_context_menu_private.h"
 #include "public/ewk_context_menu_product.h"
 #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h"
 #include "tizen/system_info.h"
-#include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/clipboard_helper_efl.h"
-#include "ui/display/screen.h"
 #include "ui/gfx/geometry/dip_util.h"
-#include "ui/gfx/geometry/rect.h"
 
 #if BUILDFLAG(IS_TIZEN)
 #include <app_control.h>
-#include <efl_extension.h>
-#include <memory>
 #endif
 
 using blink::mojom::ContextMenuDataMediaType;
-using web_contents_utils::WebViewFromWebContents;
 
 namespace {
-#define VERTICAL true
-#define HORIZONTAL false
-
-// The height of the selection menu.
-static const int kMenuHeight = 64;
-
-// Popup will be restored after kRestoreTime if it is dismissed unexpectedly.
-static const float kRestoreTime = 0.5f;
-
-const int kMinHeightVertical = 840;
-const int kMinHeightHorizontal = 420;
-
 const std::string kWebSearchLink("https://www.google.com/search?q=");
 const char* const kEditCommandUnselect = "Unselect";
 const char* const kDefaultImageExtension = "jpg";
-}
+}  // namespace
 
 namespace content {
 
-int ContextMenuControllerEfl::_popup_item_height = 96;
-std::vector<ContextMenuItemEfl> ContextMenuControllerEfl::_context_menu_listdata;
-
 ContextMenuControllerEfl::ContextMenuControllerEfl(EWebView* wv,
                                                    WebContents& web_contents)
-    : webview_(wv), web_contents_(web_contents), weak_ptr_factory_(this) {
-  native_view_ =
-      static_cast<WebContentsImpl*>(&web_contents_)->GetEflNativeView();
-}
+    : ContextMenuControllerBase(web_contents),
+      webview_(wv),
+      weak_ptr_factory_(this) {}
 
 ContextMenuControllerEfl::~ContextMenuControllerEfl() {
   for (std::set<download::DownloadItem*>::iterator it =
@@ -85,8 +57,14 @@ ContextMenuControllerEfl::~ContextMenuControllerEfl() {
        it != disk_download_items_.end(); ++it) {
     (*it)->RemoveObserver(this);
   }
-  _context_menu_listdata.clear();
-  DeletePopup();
+}
+
+// ContextMenuControllerBase override methods
+Evas_Object* ContextMenuControllerEfl::evas_object() {
+  return webview_->evas_object();
+}
+const char* ContextMenuControllerEfl::GetSelectedText() {
+  return webview_->CacheSelectedText();
 }
 
 bool ContextMenuControllerEfl::PopulateAndShowContextMenu(const ContextMenuParams &params) {
@@ -119,11 +97,6 @@ bool ContextMenuControllerEfl::PopulateAndShowContextMenu(const ContextMenuParam
   return ShowContextMenu();
 }
 
-void ContextMenuControllerEfl::Move(int x, int y) {
-  if (popup_ && is_text_selection_)
-    evas_object_move(popup_, x, y);
-}
-
 void ContextMenuControllerEfl::AddItemToProposedList(Ewk_Context_Menu_Item_Type item,
                                                      Ewk_Context_Menu_Item_Tag option,
                                                      const std::string& title,
@@ -302,384 +275,6 @@ void ContextMenuControllerEfl::GetProposedContextMenu() {
   }
 }
 
-bool ContextMenuControllerEfl::CreateContextMenu(
-    const ContextMenuParams& params) {
-  is_text_selection_ = false;
-  Eina_List* ittr;
-  void* data;
-  EINA_LIST_FOREACH(menu_items_, ittr, data) {
-    _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*> (data);
-    ContextMenuItemEfl* context_item = item->GetMenuItem();
-    switch (context_item->GetContextMenuOption()) {
-      case EWK_CONTEXT_MENU_ITEM_TAG_COPY:
-      case EWK_CONTEXT_MENU_ITEM_TAG_SELECT_ALL:
-      case EWK_CONTEXT_MENU_ITEM_TAG_SELECT_WORD:
-      case EWK_CONTEXT_MENU_ITEM_TAG_PASTE:
-        is_text_selection_ = true;
-        break;
-      default:
-        break;
-    }
-  }
-
-  if (is_text_selection_) {
-    popup_ = elm_ctxpopup_add(native_view_);
-#if BUILDFLAG(IS_TIZEN)
-    elm_ctxpopup_horizontal_set(popup_, EINA_TRUE);
-#endif
-    elm_object_tree_focus_allow_set(popup_, false);
-  } else {
-    top_widget_ = elm_object_top_widget_get(
-        elm_object_parent_widget_get(webview_->evas_object()));
-    evas_object_data_set(top_widget_, "ContextMenuContollerEfl", this);
-    popup_ = elm_popup_add(top_widget_);
-
-    if (IsMobileProfile())
-      elm_popup_align_set(popup_, ELM_NOTIFY_ALIGN_FILL, 1.0);
-    else
-      elm_popup_align_set(popup_, 0.5, 0.5);
-  }
-
-  if (!popup_)
-    return false;
-
-  if (!IsMobileProfile())
-    evas_object_smart_member_add(popup_, webview_->evas_object());
-
-  elm_object_tree_focus_allow_set(popup_, EINA_FALSE);
-
-  evas_object_data_set(popup_, "ContextMenuContollerEfl", this);
-
-  _context_menu_listdata.clear();
-  if (is_text_selection_) {
-    EINA_LIST_FOREACH(menu_items_, ittr, data) {
-      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*> (data);
-      ContextMenuItemEfl* context_item = item->GetMenuItem();
-      Elm_Object_Item* appended_item = 0;
-
-      if (!context_item->Title().empty()) {
-        appended_item = elm_ctxpopup_item_append(popup_,
-            context_item->Title().c_str(), 0, ContextMenuItemSelectedCallback,
-            context_item);
-      } else {
-        appended_item = elm_ctxpopup_item_append(popup_, 0, 0,
-            ContextMenuItemSelectedCallback,
-            context_item);
-      }
-
-      if (appended_item)
-        _context_menu_listdata.push_back(*context_item);
-    }
-#if BUILDFLAG(IS_TIZEN)
-    // Workaround
-    // Need to set "copypaste" style, to let moving handles
-    // when ctxpopup is visible
-    // http://107.108.218.239/bugzilla/show_bug.cgi?id=11613
-    elm_object_style_set(popup_, "copypaste");
-#endif
-
-    evas_object_size_hint_weight_set(popup_, EVAS_HINT_EXPAND,
-                                     EVAS_HINT_EXPAND);
-    evas_object_size_hint_max_set(popup_, -1, _popup_item_height);
-    evas_object_size_hint_min_set(popup_, 0, _popup_item_height);
-  } else {
-    std::string selected_link(params_.link_url.spec());
-    if (!selected_link.empty()) {
-      if (base::StartsWith(selected_link, std::string("mailto:"),
-                           base::CompareCase::INSENSITIVE_ASCII) ||
-          base::StartsWith(selected_link, std::string("tel:"),
-                           base::CompareCase::INSENSITIVE_ASCII)) {
-        size_t current_pos = 0;
-        size_t next_delimiter = 0;
-        // Remove the First part and Extract actual URL or title
-        // (mailto:abc@d.com?id=345  ==> abc@d.com?id=345)
-        if ((next_delimiter = selected_link.find(":", current_pos)) !=
-            std::string::npos) {
-          current_pos = next_delimiter + 1;
-        }
-
-        std::string last_part = selected_link.substr(current_pos);
-        std::string core_part = selected_link.substr(current_pos);
-        current_pos = 0;
-        next_delimiter = 0;
-        // Trim the core by removing ? at the end (abc@d.com?id=345  ==>
-        // abc@d.com)
-        if ((next_delimiter = core_part.find("?", current_pos)) !=
-            std::string::npos) {
-          last_part =
-              core_part.substr(current_pos, next_delimiter - current_pos);
-        }
-      } else {
-        elm_object_part_text_set(popup_, "title,text", selected_link.c_str());
-      }
-    } else {
-      std::string selected_image(params_.src_url.spec());
-      if (!selected_image.empty())
-        elm_object_part_text_set(popup_, "title,text", selected_image.c_str());
-    }
-
-    evas_object_size_hint_weight_set(popup_, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
-
-    list_ = elm_list_add(popup_);
-    elm_list_mode_set(list_, ELM_LIST_EXPAND);
-    evas_object_data_set(list_, "ContextMenuContollerEfl", this);
-
-    Eina_List* ittr;
-    void* data;
-    EINA_LIST_FOREACH(menu_items_, ittr, data) {
-      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*> (data);
-      ContextMenuItemEfl* context_item = item->GetMenuItem();
-      if (!context_item->Title().empty()) {
-        Elm_Object_Item* appended_item = elm_list_item_append(
-            list_, context_item->Title().c_str(), NULL, NULL,
-            ContextMenuItemSelectedCallback, context_item);
-
-        if (appended_item)
-          _context_menu_listdata.push_back(*context_item);
-      }
-    }
-
-    ContextMenuPopupListResize();
-
-    evas_object_show(list_);
-    elm_object_content_set(popup_, list_);
-    evas_object_event_callback_add(popup_, EVAS_CALLBACK_RESIZE,
-                                   ContextMenuPopupResize, this);
-  }
-
-  if (_context_menu_listdata.empty()) {
-    DeletePopup();
-    return false;
-  }
-  return true;
-}
-
-void ContextMenuControllerEfl::ContextMenuHWBackKey(void* data, Evas_Object* obj,
-                                                    void* event_info) {
-  auto menu_controller = static_cast<ContextMenuControllerEfl*>(data);
-  if (menu_controller) {
-    auto selection_controller =
-        menu_controller->webview_->GetSelectionController();
-
-    if (selection_controller)
-      selection_controller->ToggleCaretAfterSelection();
-
-    menu_controller->HideContextMenu();
-    evas_object_data_del(obj, "ContextEfl");
-  }
-}
-
-// static
-Eina_Bool ContextMenuControllerEfl::RestoreTimerCallback(void* data) {
-  auto* menu_controller = static_cast<ContextMenuControllerEfl*>(data);
-  if (!menu_controller)
-    return ECORE_CALLBACK_CANCEL;
-
-  auto selection_controller =
-      menu_controller->webview_->GetSelectionController();
-
-  if (menu_controller->popup_ && selection_controller &&
-      selection_controller->GetSelectionStatus() &&
-      !selection_controller->IsCaretMode()) {
-    evas_object_show(menu_controller->popup_);
-  }
-
-  menu_controller->restore_timer_ = nullptr;
-
-  return ECORE_CALLBACK_CANCEL;
-}
-
-void ContextMenuControllerEfl::ContextMenuCancelCallback(void* data, Evas_Object* obj,
-                                                         void* event_info) {
-  ContextMenuControllerEfl* menu_controller =
-      static_cast<ContextMenuControllerEfl*>(
-          evas_object_data_get(obj, "ContextMenuContollerEfl"));
-  if (!menu_controller)
-    return;
-
-  RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
-      menu_controller->web_contents_.GetRenderWidgetHostView());
-  if (!rwhva)
-    return;
-
-  // context_menu is dismissed when we touch outside of popup.
-  // It is policy of elm_ctxpopup by EFL team.
-  // But according to wcs TC(21~23) context_menu should be displayed,
-  // when touchevent is consumed. So we should show context_menu again when
-  // touchevent is consumed.
-  if (rwhva->offscreen_helper()->IsTouchstartConsumed() ||
-      rwhva->offscreen_helper()->IsTouchendConsumed()) {
-    evas_object_show(menu_controller->popup_);
-    return;
-  }
-
-  // TODO: If |elm_ctxpopup_auto_hide_disabled_set| is works properly,
-  // We should remove this timer.
-  // Bug: http://suprem.sec.samsung.net/jira/browse/TNEXT-67
-  menu_controller->restore_timer_ =
-      ecore_timer_add(kRestoreTime, RestoreTimerCallback, menu_controller);
-}
-
-void ContextMenuControllerEfl::ContextMenuItemSelectedCallback(void* data,
-    Evas_Object* obj, void* event_info) {
-  Evas_Object* pop_up = obj;
-  ContextMenuControllerEfl* menu_controller =
-      static_cast<ContextMenuControllerEfl*>
-      (evas_object_data_get(pop_up, "ContextMenuContollerEfl"));
-
-  if (menu_controller) {
-    ContextMenuItemEfl* selected_menu_item = static_cast<ContextMenuItemEfl*>(data);
-    if (selected_menu_item)
-       menu_controller->MenuItemSelected(selected_menu_item);
-  }
-}
-
-void ContextMenuControllerEfl::RequestSelectionRect() const {
-  auto rwhva = static_cast<RenderWidgetHostViewAura*>(
-      web_contents_.GetRenderWidgetHostView());
-  CHECK(rwhva);
-  rwhva->host()->RequestSelectionRect();
-}
-
-gfx::Point ContextMenuControllerEfl::CalculateSelectionMenuPosition(
-    const gfx::Rect& selection_rect) {
-  auto* selection_controller = webview_->GetSelectionController();
-  if (!selection_controller)
-    return gfx::Point();
-
-  auto visible_viewport_rect = selection_controller->GetVisibleViewportRect();
-  auto forbidden_rect =
-      selection_controller->GetForbiddenRegionRect(selection_rect);
-  gfx::Point position;
-  // Selection menu should always be placed at the centre of visible selection
-  // area horizontally.
-  position.set_x(forbidden_rect.CenterPoint().x());
-
-  if (forbidden_rect.y() > visible_viewport_rect.y() + kMenuHeight) {
-    // If possible, selection menu should always be placed above forbidden
-    // region.
-    position.set_y(forbidden_rect.y());
-  } else if (forbidden_rect.bottom() + kMenuHeight <
-             visible_viewport_rect.bottom()) {
-    // If there is no sufficient space above, we're trying to place selection
-    // menu below forbidden region.
-    position.set_y(forbidden_rect.bottom() + kMenuHeight);
-  } else {
-    // If there is no sufficient space above and below, selection menu will be
-    // shown at the centre of visible selection area vertically.
-    position.set_y(forbidden_rect.CenterPoint().y());
-  }
-
-  return position;
-}
-
-void ContextMenuControllerEfl::OnSelectionRectReceived(
-    gfx::Rect selection_rect) {
-  auto rwhva = static_cast<RenderWidgetHostViewAura*>(
-      web_contents_.GetRenderWidgetHostView());
-  auto controller = webview_->GetSelectionController();
-
-  if (IsMobileProfile() && (context_menu_status_ != INPROGRESS))
-    return;
-  if (!popup_ || !rwhva || !controller)
-    return;
-
-  float device_scale =
-      display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
-  gfx::Rect selection_rect_dip = gfx::ToEnclosingRect(
-      ConvertRectToPixels(gfx::Rect(selection_rect), device_scale));
-
-  // Consider a webview offset.
-  selection_rect_dip.Offset(
-      rwhva->offscreen_helper()->GetViewBoundsInPix().OffsetFromOrigin());
-
-  auto visible_viewport_rect = controller->GetVisibleViewportRect();
-
-  // In case of the caret mode selection_rect is empty
-  if (!webview_->GetSelectionController()->IsCaretMode() &&
-      gfx::IntersectRects(visible_viewport_rect, selection_rect_dip)
-          .IsEmpty()) {
-    return;
-  }
-
-  gfx::Point selection_menu_position =
-      CalculateSelectionMenuPosition(selection_rect_dip);
-  if (!visible_viewport_rect.Contains(selection_menu_position))
-    return;
-
-  evas_object_smart_callback_add(popup_, "dismissed", ContextMenuCancelCallback,
-                                 this);
-
-  if (IsMobileProfile())
-    elm_ctxpopup_auto_hide_disabled_set(popup_, EINA_TRUE);
-
-#if BUILDFLAG(IS_TIZEN)
-  eext_object_event_callback_add(popup_, EEXT_CALLBACK_BACK,
-                                 ContextMenuHWBackKey, this);
-#endif
-  evas_object_move(popup_, selection_menu_position.x(),
-                   selection_menu_position.y());
-
-  controller->ContextMenuStatusVisible();
-
-  evas_object_show(popup_);
-  evas_object_raise(popup_);
-  if (IsMobileProfile())
-    context_menu_status_ = VISIBLE;
-}
-
-bool ContextMenuControllerEfl::ShowContextMenu() {
-  if (!popup_)
-    return false;
-
-  if (IsMobileProfile())
-    context_menu_status_ = INPROGRESS;
-
-  if (is_text_selection_) {
-    // Selection menu will be shown in OnSelectionRectReceived after receiving
-    // selection rectangle form the renderer.
-    RequestSelectionRect();
-  } else {
-    evas_object_smart_callback_add(popup_, "block,clicked",
-                                   BlockClickedCallback, this);
-
-#if BUILDFLAG(IS_TIZEN)
-    eext_object_event_callback_add(popup_, EEXT_CALLBACK_BACK, ContextMenuHWBackKey, this);
-    eext_object_event_callback_add(popup_, EEXT_CALLBACK_MORE,
-                                   ContextMenuHWBackKey, this);
-#endif
-    evas_object_show(popup_);
-    evas_object_raise(popup_);
-    if (IsMobileProfile())
-      context_menu_status_ = VISIBLE;
-  }
-  return true;
-}
-
-void ContextMenuControllerEfl::HideSelectionHandle() {
-  SelectionControllerEfl* controller = webview_->GetSelectionController();
-  if (controller)
-    controller->HideHandles();
-}
-
-void ContextMenuControllerEfl::ClearSelection() {
-  auto controller = webview_->GetSelectionController();
-  if (controller)
-    controller->ClearSelection();
-}
-
-void ContextMenuControllerEfl::RequestShowSelectionHandleAndContextMenu(
-    bool trigger_selection_change) {
-  SelectionControllerEfl* controller = webview_->GetSelectionController();
-  if (controller) {
-    controller->SetSelectionStatus(true);
-    controller->SetWaitsForRendererSelectionChanges(
-        SelectionControllerEfl::Reason::RequestedByContextMenu);
-    if (trigger_selection_change)
-      controller->TriggerOnSelectionChange();
-  }
-}
-
 void ContextMenuControllerEfl::OnClipboardDownload(
     download::DownloadItem* item,
     download::DownloadInterruptReason interrupt_reason) {
@@ -813,10 +408,6 @@ void ContextMenuControllerEfl::OpenURL(
 }
 
 #if BUILDFLAG(IS_TIZEN)
-static void destroy_app_handle(app_control_h* handle) {
-  std::ignore = app_control_destroy(*handle);
-}
-
 void ContextMenuControllerEfl::LaunchBrowserWithWebSearch(
     const std::string& text) {
   app_control_h app_control;
@@ -834,86 +425,8 @@ void ContextMenuControllerEfl::LaunchBrowserWithWebSearch(
 
   app_control_destroy(app_control);
 }
-
-void ContextMenuControllerEfl::LaunchShareApp(const std::string& text) {
-  app_control_h handle = nullptr;
-  int error_code = app_control_create(&handle);
-  if (error_code < 0 || !handle) {
-    LOG(ERROR) << __PRETTY_FUNCTION__
-               << " : Failed to create share service. Error code: "
-               << error_code;
-    return;
-  }
-
-  std::unique_ptr<app_control_h, decltype(&destroy_app_handle)> handle_ptr(
-      &handle, destroy_app_handle);
-
-  error_code =
-      app_control_set_operation(handle, APP_CONTROL_OPERATION_SHARE_TEXT);
-  if (error_code < 0) {
-    LOG(ERROR) << __PRETTY_FUNCTION__
-               << ": Failed to set share text operation. Error code: "
-               << error_code;
-    return;
-  }
-
-  error_code =
-      app_control_add_extra_data(handle, APP_CONTROL_DATA_TEXT, text.c_str());
-  if (error_code < 0) {
-    LOG(ERROR) << __PRETTY_FUNCTION__
-               << ": Failed to add data for share application. Error code: "
-               << error_code;
-    return;
-  }
-
-  error_code = app_control_send_launch_request(handle, nullptr, nullptr);
-  if (error_code < 0) {
-    LOG(ERROR) << __PRETTY_FUNCTION__
-               << ": Failed to send launch request for share application. "
-               << "Error code: " << error_code;
-    return;
-  }
-}
 #endif
 
-void ContextMenuControllerEfl::ContextMenuPopupResize(void* data,
-                                                      Evas* e,
-                                                      Evas_Object* obj,
-                                                      void* event_info) {
-  auto thiz = static_cast<ContextMenuControllerEfl*>(data);
-  if (thiz)
-    thiz->ContextMenuPopupListResize();
-}
-
-void ContextMenuControllerEfl::BlockClickedCallback(void* data, Evas_Object*, void*)
-{
-  ContextMenuControllerEfl* menu_controller = static_cast<ContextMenuControllerEfl*>(data);
-
-  if (!menu_controller)
-    return;
-
-  menu_controller->HideContextMenu();
-}
-
-void ContextMenuControllerEfl::ContextMenuPopupListResize() {
-  if (!IsMobileProfile())
-    return;
-
-  int orientation = webview_->GetOrientation();
-  if ((orientation == 90 || orientation == 270) &&
-      _context_menu_listdata.size() > 3) {
-    evas_object_size_hint_min_set(list_, 0, kMinHeightHorizontal);
-  } else if ((orientation == 0 || orientation == 180) &&
-             _context_menu_listdata.size() > 7) {
-    evas_object_size_hint_min_set(list_, 0, kMinHeightVertical);
-  } else {
-    evas_object_size_hint_min_set(
-        list_, 0, _context_menu_listdata.size() * _popup_item_height);
-  }
-
-  evas_object_show(list_);
-}
-
 void ContextMenuControllerEfl::CustomMenuItemSelected(ContextMenuItemEfl* menu_item) {
   _Ewk_Context_Menu_Item item(new ContextMenuItemEfl(menu_item->GetContextMenuOptionType(),
                                                      menu_item->GetContextMenuOption(),
@@ -926,10 +439,7 @@ void ContextMenuControllerEfl::CustomMenuItemSelected(ContextMenuItemEfl* menu_i
 
 void ContextMenuControllerEfl::MenuItemSelected(ContextMenuItemEfl* menu_item)
 {
-  if (!menu_item)
-    return;
-
-  if (!webview_)
+  if (!menu_item || !webview_)
     return;
 
   HideContextMenu();
@@ -1135,41 +645,6 @@ void ContextMenuControllerEfl::MenuItemSelected(ContextMenuItemEfl* menu_item)
   }
 }
 
-void ContextMenuControllerEfl::HideContextMenu() {
-  if (IsMobileProfile() &&
-      (context_menu_status_ == HIDDEN || context_menu_status_ == NONE))
-    return;
-
-  if (is_text_selection_) {
-    SelectionControllerEfl* controller = webview_->GetSelectionController();
-    if (controller)
-      controller->ContextMenuStatusHidden();
-  }
-
-  if (popup_) {
-    evas_object_event_callback_del(popup_, EVAS_CALLBACK_RESIZE,
-                                   ContextMenuPopupResize);
-    evas_object_del(popup_);
-    popup_ = nullptr;
-  }
-
-  if (restore_timer_) {
-    ecore_timer_del(restore_timer_);
-    restore_timer_ = nullptr;
-  }
-
-  if (IsMobileProfile()) {
-    context_menu_status_ = HIDDEN;
-  } else if (menu_items_) {
-    void* data;
-    EINA_LIST_FREE(menu_items_, data) {
-      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*>(data);
-      delete item;
-    }
-    menu_items_ = nullptr;
-  }
-}
-
 void ContextMenuControllerEfl::Resize(const gfx::Rect& webview_rect) {
   if (save_fail_dialog_)
     save_fail_dialog_->SetPopupSize(webview_rect.width(),
@@ -1178,47 +653,6 @@ void ContextMenuControllerEfl::Resize(const gfx::Rect& webview_rect) {
     file_saved_dialog_->SetPopupSize(webview_rect.width(),
                                      webview_rect.height());
 
-  if (!popup_)
-    return;
-
-  if (IsMobileProfile() && context_menu_status_ != VISIBLE)
-    return;
-
-  if (is_text_selection_) {
-    RequestSelectionRect();
-  } else {
-    evas_object_resize(popup_, webview_rect.width(), webview_rect.height());
-  }
-}
-
-void ContextMenuControllerEfl::DeletePopup() {
-  if (top_widget_)
-    evas_object_data_set(top_widget_, "ContextMenuContollerEfl", 0);
-
-  if (is_text_selection_) {
-    SelectionControllerEfl* controller = webview_->GetSelectionController();
-    if (controller)
-      controller->ContextMenuStatusHidden();
-  }
-
-  if (popup_) {
-    evas_object_data_set(popup_, "ContextMenuContollerEfl", 0);
-    if (!IsMobileProfile())
-      evas_object_smart_member_del(popup_);
-    evas_object_del(popup_);
-    popup_ = nullptr;
-    list_ = nullptr;
-  }
-
-  if (menu_items_) {
-    void* data;
-    EINA_LIST_FREE(menu_items_, data) {
-      _Ewk_Context_Menu_Item *item = static_cast<_Ewk_Context_Menu_Item*> (data);
-      delete item;
-    }
-    menu_items_ = nullptr;
-  }
-  if (IsMobileProfile())
-    context_menu_status_ = NONE;
+  ContextMenuControllerBase::Resize(webview_rect);
 }
 }
index 1c898d2..cab9536 100644 (file)
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef context_menu_controller_h
-#define context_menu_controller_h
+#ifndef context_menu_controller_efl_h
+#define context_menu_controller_efl_h
+
+#include "content/browser/context_menu/context_menu_controller_base.h"
 
-#include <Evas.h>
 #include <set>
 
 #include "base/memory/weak_ptr.h"
 #include "browser/javascript_modal_dialog_efl.h"
 #include "components/download/public/common/download_item.h"
 #include "components/download/public/common/download_url_parameters.h"
-#include "content/public/browser/context_menu_params.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
-#include "public/ewk_context_menu_internal.h"
+#include "ui/base/window_open_disposition.h"
 
 class EWebView;
 
-namespace gfx {
-class Rect;
-}
-
 namespace content {
 
-class ContextMenuItemEfl {
+class ContextMenuControllerEfl : public ContextMenuControllerBase,
+                                 public download::DownloadItem::Observer {
  public:
-  ContextMenuItemEfl(Ewk_Context_Menu_Item_Type item,
-                     Ewk_Context_Menu_Item_Tag option,
-                     const std::string& title,
-                     const std::string& image_url = std::string(),
-                     const std::string& link_url = std::string(),
-                     const std::string& icon_path = std::string())
-    : menu_type_(item)
-    , menu_option_(option)
-    , title_(title)
-    , is_enabled_(true)
-    , image_url_(image_url)
-    , link_url_(link_url)
-    , icon_path_(icon_path) {
-  }
-
-  ~ContextMenuItemEfl() { }
-
-  const std::string& Title() const { return title_; }
-  void SetTitle(const std::string& title) { title_ = title; }
-
-  bool IsEnabled() const { return is_enabled_; }
-  void SetEnabled(bool status) { is_enabled_ = status; }
-  Ewk_Context_Menu_Item_Tag GetContextMenuOption() const { return menu_option_; }
-  Ewk_Context_Menu_Item_Type GetContextMenuOptionType() const { return menu_type_; }
-  const std::string& LinkURL() const { return link_url_; }
-  const std::string& ImageURL() const { return image_url_; }
-  const std::string& IconPath() const { return icon_path_; }
-
- private:
-  Ewk_Context_Menu_Item_Type menu_type_;
-  Ewk_Context_Menu_Item_Tag menu_option_;
-  std::string title_;
-  bool is_enabled_;
-  std::string image_url_;
-  std::string link_url_;
-  std::string icon_path_;
-};
-
-class ContextMenuControllerEfl : public download::DownloadItem::Observer {
- public:
-  static void ContextMenuCancelCallback(void* data, Evas_Object* obj, void* event_info);
-  static void ContextMenuItemSelectedCallback(void* data, Evas_Object* obj, void* event_info);
-  static void ContextMenuHWBackKey(void* data, Evas_Object* obj, void* event_info);
-
   ContextMenuControllerEfl(EWebView* wv, WebContents& web_contents);
   ~ContextMenuControllerEfl();
 
+  ContextMenuControllerEfl(const ContextMenuControllerEfl&) = delete;
+  ContextMenuControllerEfl& operator=(const ContextMenuControllerEfl&) = delete;
+
   bool PopulateAndShowContextMenu(const ContextMenuParams& params);
-  void Move(int x, int y);
-  void MenuItemSelected(ContextMenuItemEfl* menu_item);
-  void HideContextMenu();
+  void MenuItemSelected(ContextMenuItemEfl* menu_item) override;
   gfx::Point GetContextMenuShowPos() const { return context_menu_show_pos_; };
-  void Resize(const gfx::Rect& webview_rect);
-
-  void RequestSelectionRect() const;
-  void OnSelectionRectReceived(gfx::Rect selection_rect);
-  gfx::Point CalculateSelectionMenuPosition(const gfx::Rect& selection_rect);
+  void Resize(const gfx::Rect& webview_rect) override;
 
  private:
-  static void ContextMenuPopupResize(void* data,
-                                     Evas* e,
-                                     Evas_Object* obj,
-                                     void* event_info);
-  enum ContextMenuStatusTag { NONE = 0, HIDDEN, INPROGRESS, VISIBLE };
-  static void BlockClickedCallback(void*, Evas_Object*, void*);
-  void ContextMenuPopupListResize();
+  // ContextMenuControllerBase
+  Evas_Object* evas_object() override;
+  const char* GetSelectedText() override;
+
   void CustomMenuItemSelected(ContextMenuItemEfl*);
   void GetProposedContextMenu();
-  bool CreateContextMenu(const ContextMenuParams& params);
-  bool ShowContextMenu();
   void AddItemToProposedList(Ewk_Context_Menu_Item_Type item,
                              Ewk_Context_Menu_Item_Tag option,
                              const std::string& title,
                              const std::string& image_url = std::string(),
                              const std::string& link_url = std::string(),
                              const std::string& icon_path = std::string());
-  void HideSelectionHandle();
-  void ClearSelection();
-  void RequestShowSelectionHandleAndContextMenu(
-      bool trigger_selection_change = false);
   virtual void OnDownloadUpdated(download::DownloadItem* download) override;
   void OnClipboardDownload(download::DownloadItem* item,
                            download::DownloadInterruptReason interrupt_reason);
@@ -121,34 +60,17 @@ class ContextMenuControllerEfl : public download::DownloadItem::Observer {
   void OpenURL(const GURL url, const WindowOpenDisposition disposition);
 #if BUILDFLAG(IS_TIZEN)
   void LaunchBrowserWithWebSearch(const std::string& text);
-  void LaunchShareApp(const std::string& text);
 #endif
-  void DeletePopup();
-
-  static Eina_Bool RestoreTimerCallback(void* data);
 
-  static std::vector<ContextMenuItemEfl> _context_menu_listdata;
-  static int _popup_item_height;
-  static bool _context_menu_resized;
   EWebView* webview_;
-  Evas_Object* native_view_;
-  Evas_Object* top_widget_ = nullptr;
-  Evas_Object* popup_ = nullptr;
-  Evas_Object* list_ = nullptr;
-  Eina_List* menu_items_ = nullptr;
-  ContextMenuParams params_;
-  WebContents& web_contents_;
   gfx::Point context_menu_show_pos_;
-  bool is_text_selection_ = false;
   std::set<download::DownloadItem*> clipboard_download_items_;
   std::set<download::DownloadItem*> disk_download_items_;
   std::unique_ptr<JavaScriptModalDialogEfl> file_saved_dialog_;
   std::unique_ptr<JavaScriptModalDialogEfl> save_fail_dialog_;
-  Ecore_Timer* restore_timer_ = nullptr;
-  ContextMenuStatusTag context_menu_status_ = NONE;
 
   base::WeakPtrFactory<ContextMenuControllerEfl> weak_ptr_factory_;
 };
 
 } // namespace
-#endif  // context_menu_controller_h
+#endif  // context_menu_controller_efl_h
diff --git a/tizen_src/ewk/efl_integration/private/ewk_context_menu_item_private.h b/tizen_src/ewk/efl_integration/private/ewk_context_menu_item_private.h
new file mode 100644 (file)
index 0000000..f2f548a
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2022 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ewk_context_menu_item_private_h
+#define ewk_context_menu_item_private_h
+
+namespace content {
+class ContextMenuItemEfl;
+}
+
+class _Ewk_Context_Menu_Item {
+ public:
+  _Ewk_Context_Menu_Item(content::ContextMenuItemEfl* menu_item)
+      : menu_item_(menu_item) {}
+
+  ~_Ewk_Context_Menu_Item() { delete menu_item_; }
+
+  _Ewk_Context_Menu_Item(const _Ewk_Context_Menu_Item&) = delete;
+  _Ewk_Context_Menu_Item& operator=(const _Ewk_Context_Menu_Item&) = delete;
+
+  content::ContextMenuItemEfl* GetMenuItem() const { return menu_item_; }
+
+ private:
+  content::ContextMenuItemEfl* menu_item_;
+};
+
+#endif  // ewk_context_menu_private_h
index ed5778b..216b29d 100644 (file)
@@ -9,24 +9,7 @@
 #include <stdio.h>
 
 #include "context_menu_controller_efl.h"
-
-class _Ewk_Context_Menu_Item {
- public:
-  _Ewk_Context_Menu_Item(content::ContextMenuItemEfl* menu_item)
-      : menu_item_(menu_item) {
-  }
-
-  ~_Ewk_Context_Menu_Item() {
-    delete menu_item_;
-  }
-
-  content::ContextMenuItemEfl* GetMenuItem() const {
-    return menu_item_;
-  }
- private:
-  content::ContextMenuItemEfl* menu_item_;
-};
-
+#include "ewk_context_menu_item_private.h"
 
 // Wrapper for context_menu_controller items
 class _Ewk_Context_Menu {