[M108 Migration][ContextPopup] Bring up Context Popup 2/2 18/288618/8
authorBakka Uday Kiran <b.kiran@samsung.com>
Mon, 20 Feb 2023 11:12:11 +0000 (16:42 +0530)
committerBot Blink <blinkbot@samsung.com>
Mon, 27 Feb 2023 03:48:09 +0000 (03:48 +0000)
This patch migrates changes related to context popup bringup and
Also handles,

1. Displaying context menu(text_selection) when touchevent is consumed.
2. Simplify logic of text selection.
3. Correcting context menu Show/Hide behaviour by maintaining state
   machine.
4. Fix for scrolling not smooth when context menu is shown.
5. Remove focusing codes in ContextMenuControllerEfl.
6. Check the status of context menu just before display.
7. Adjust the position of context menu for text selection

Reference: https://review.tizen.org/gerrit/c/282372

Change-Id: I6d66c9c0ea4f96c602378515434af23a62ca37f6
Signed-off-by: Bakka Uday Kiran <b.kiran@samsung.com>
19 files changed:
content/browser/renderer_host/render_widget_host_view_aura.cc
third_party/blink/common/context_menu_data/context_menu_mojom_traits.cc
third_party/blink/common/context_menu_data/context_menu_params_builder.cc
third_party/blink/common/context_menu_data/untrustworthy_context_menu_params.cc
third_party/blink/public/common/context_menu_data/context_menu_data.h
third_party/blink/public/common/context_menu_data/context_menu_mojom_traits.h
third_party/blink/public/common/context_menu_data/untrustworthy_context_menu_params.h
third_party/blink/public/mojom/context_menu/context_menu.mojom
third_party/blink/renderer/core/page/context_menu_controller.cc
tizen_src/chromium_impl/content/browser/renderer_host/rwhv_aura_offscreen_helper_efl.cc
tizen_src/chromium_impl/content/browser/renderer_host/rwhv_aura_offscreen_helper_efl.h
tizen_src/chromium_impl/content/browser/selection/selection_controller_efl.cc
tizen_src/chromium_impl/content/browser/selection/selection_controller_efl.h
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/download_manager_delegate_efl.cc
tizen_src/ewk/efl_integration/download_manager_delegate_efl.h
tizen_src/ewk/efl_integration/eweb_view.cc
tizen_src/ewk/efl_integration/eweb_view.h

index 21d097e..398f442 100644 (file)
@@ -1187,6 +1187,14 @@ void RenderWidgetHostViewAura::ProcessAckedTouchEvent(
       break;
   }
 
+#if BUILDFLAG(IS_EFL)
+  if (touch.event.GetType() == blink::WebInputEvent::Type::kTouchStart)
+    offscreen_helper_->SetTouchStartConsumed(result == ui::ER_HANDLED);
+
+  if (touch.event.GetType() == blink::WebInputEvent::Type::kTouchEnd)
+    offscreen_helper_->SetTouchEndConsumed(result == ui::ER_HANDLED);
+#endif
+
   // Only send acks for one changed touch point.
   bool sent_ack = false;
   for (size_t i = 0; i < touch.event.touches_length; ++i) {
index 4a34262..116fc00 100644 (file)
@@ -61,6 +61,7 @@ bool StructTraits<blink::mojom::UntrustworthyContextMenuParamsDataView,
   out->is_editable = data.is_editable();
 #if BUILDFLAG(IS_EFL)
   out->is_text_node = data.is_text_node();
+  out->is_user_select_none = data.is_user_select_none();
 #endif
 
 #if BUILDFLAG(IS_TIZEN)
index b9532c7..9332437 100644 (file)
@@ -57,6 +57,7 @@ UntrustworthyContextMenuParams ContextMenuParamsBuilder::Build(
   params.is_editable = data.is_editable;
 #if BUILDFLAG(IS_EFL)
   params.is_text_node = data.is_text_node;
+  params.is_user_select_none = !data.is_selectable;
 #endif
 
 #if BUILDFLAG(IS_TIZEN)
index 5e99669..5167a97 100644 (file)
@@ -64,6 +64,7 @@ void UntrustworthyContextMenuParams::Assign(
   is_editable = other.is_editable;
 #if BUILDFLAG(IS_EFL)
   is_text_node = other.is_text_node;
+  is_user_select_none = other.is_user_select_none;
 #endif
 
 #if BUILDFLAG(IS_TIZEN)
index 0168924..81c2a97 100644 (file)
@@ -119,6 +119,12 @@ struct ContextMenuData {
 
 #if BUILDFLAG(IS_EFL)
   bool is_text_node;
+
+  // Whether element has -webkit-user-select: none attribute.
+  bool is_user_select_none;
+
+  // Whether the node is selectable.
+  bool is_selectable = false;
 #endif
 
 #if BUILDFLAG(IS_TIZEN)
index 3fc5f80..57cb588 100644 (file)
@@ -119,6 +119,11 @@ struct BLINK_COMMON_EXPORT
   static bool is_text_node(const blink::UntrustworthyContextMenuParams& r) {
     return r.is_text_node;
   }
+
+  static bool is_user_select_none(
+      const blink::UntrustworthyContextMenuParams& r) {
+    return r.is_user_select_none;
+  }
 #endif
 
 #if BUILDFLAG(IS_TIZEN)
index d1676b0..2d1bd14 100644 (file)
@@ -106,6 +106,9 @@ struct BLINK_COMMON_EXPORT UntrustworthyContextMenuParams {
 #if BUILDFLAG(IS_EFL)
   // Whether node context menu was invoked on is a text node.
   bool is_text_node;
+
+  // Whether element has -webkit-user-select: none attribute.
+  bool is_user_select_none;
 #endif
 
 #if BUILDFLAG(IS_TIZEN)
index 60259f1..7200b7d 100644 (file)
@@ -175,6 +175,10 @@ struct UntrustworthyContextMenuParams {
   [EnableIf=is_tizen]
   bool is_draggable;
 
+  // Whether element has -webkit-user-select: none attribute.
+  [EnableIf=is_efl]
+  bool is_user_select_none;
+
   // Writing direction menu items.
   int32 writing_direction_default;
   int32 writing_direction_left_to_right;
index 854831a..8dfb5e5 100644 (file)
@@ -690,6 +690,9 @@ bool ContextMenuController::ShowContextMenu(LocalFrame* frame,
   }
 
 #if BUILDFLAG(IS_EFL)
+  data.is_selectable = result.InnerNode()->GetLayoutObject() &&
+                       result.InnerNode()->GetLayoutObject()->IsSelectable();
+
   data.is_text_node = result.InnerNode()->IsTextNode();
 #endif
 
index ee3e3f8..8b1e6ef 100644 (file)
@@ -1321,6 +1321,13 @@ void RWHVAuraOffscreenHelperEfl::SendGestureEvent(
     return;
   }
 
+  // TODO: In this case, double tap should be filtered in
+  // TouchDispositionGestureFilter. (See http://goo.gl/5G8PWJ)
+  if (event_type == blink::WebInputEvent::Type::kGestureDoubleTap &&
+      touchend_consumed_) {
+    return;
+  }
+
   if (event_type != blink::WebInputEvent::Type::kUndefined) {
     rwhv_aura_->host()->ForwardGestureEventWithLatencyInfo(
         event, CreateLatencyInfo(event));
@@ -1487,4 +1494,11 @@ void RWHVAuraOffscreenHelperEfl::UpdateEdgeEffect() {
   EnsureEdgeEffect().UpdateRect();
 }
 
+void RWHVAuraOffscreenHelperEfl::SetTouchStartConsumed(
+    bool touchstart_consumed) {
+  touchstart_consumed_ = touchstart_consumed;
+}
+void RWHVAuraOffscreenHelperEfl::SetTouchEndConsumed(bool touchend_consumed) {
+  touchend_consumed_ = touchend_consumed;
+}
 }  // namespace content
index 42ad061..a1ce4e0 100644 (file)
@@ -74,6 +74,11 @@ class CONTENT_EXPORT RWHVAuraOffscreenHelperEfl {
   gfx::Size GetPhysicalBackingSize() const;
   void OnGetFocusedNodeBounds(const gfx::RectF& rect);
 
+  void SetTouchStartConsumed(bool touchstart_consumed);
+  void SetTouchEndConsumed(bool touchend_consumed);
+  bool IsTouchstartConsumed() const { return touchstart_consumed_; }
+  bool IsTouchendConsumed() const { return touchend_consumed_; }
+
   void SetTouchEventsEnabled(bool enabled);
   bool TouchEventsEnabled();
 
@@ -254,6 +259,9 @@ class CONTENT_EXPORT RWHVAuraOffscreenHelperEfl {
 
   bool magnifier_ = false;
 
+  bool touchstart_consumed_ = false;
+  bool touchend_consumed_ = false;
+
   OnFocusCallback on_focus_in_callback_;
   OnFocusCallback on_focus_out_callback_;
 
index d6e172f..efca84b 100644 (file)
 #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_impl_efl.h"
+#include "content/browser/web_contents/web_contents_view_aura.h"
 #include "content/public/browser/context_menu_params.h"
 #include "content/public/browser/web_contents.h"
+#include "tizen/system_info.h"
 #include "ui/base/clipboard/clipboard_helper_efl.h"
 #include "ui/display/device_display_info_efl.h"
 #include "ui/display/screen.h"
@@ -74,8 +76,7 @@ SelectionControllerEfl::SelectionControllerEfl(RenderWidgetHostViewAura* rwhva)
           new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_RIGHT)),
       input_handle_(
           new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_INPUT)),
-      magnifier_(new SelectionMagnifierEfl(this)),
-      selection_mode_(None) {
+      magnifier_(new SelectionMagnifierEfl(this)) {
   evas_object_event_callback_add(rwhva_->offscreen_helper()->content_image(),
                                  EVAS_CALLBACK_MOVE,
                                  &EvasParentViewMoveCallback, this);
@@ -131,9 +132,9 @@ bool SelectionControllerEfl::GetSelectionEditable() const {
 }
 
 bool SelectionControllerEfl::GetCaretSelectionStatus() const {
-  TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
-               "caret selection", selection_mode_ == Caret);
-  return selection_mode_ == Caret;
+  TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__, "caret selection",
+               selection_mode_ == SelectionMode::CARET);
+  return selection_mode_ == SelectionMode::CARET;
 }
 
 void SelectionControllerEfl::SetControlsTemporarilyHidden(
@@ -277,11 +278,11 @@ void SelectionControllerEfl::DetermineSelectionMode(
     const gfx::Rect& right_rect) {
 
   if (left_rect == gfx::Rect() && right_rect == gfx::Rect())
-    SetSelectionMode(None);
+    SetSelectionMode(SelectionMode::NONE);
   else if (left_rect == right_rect && GetSelectionEditable())
-    SetSelectionMode(Caret);
+    SetSelectionMode(SelectionMode::CARET);
   else
-    SetSelectionMode(Range);
+    SetSelectionMode(SelectionMode::RANGE);
 }
 
 bool SelectionControllerEfl::UpdateSelectionDataAndShow(
@@ -315,11 +316,17 @@ bool SelectionControllerEfl::UpdateSelectionDataAndShow(
   return true;
 }
 
+bool SelectionControllerEfl::IsCaretMode() const {
+  return selection_data_->IsInEditField() && GetLeftRect() == GetRightRect();
+}
+
 void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired(
     Reason explicit_reason) {
   TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
 
+  Reason saved_reason = selection_change_reason_;
   Reason effective_reason = selection_change_reason_;
+
   if (explicit_reason != Reason::Irrelevant)
     effective_reason = explicit_reason;
 
@@ -340,8 +347,25 @@ void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired(
     return;
   }
 
-  bool is_selection_range_visible =
-      start_selection_.visible() || end_selection_.visible();
+  bool is_start_selection_visible = start_selection_.visible();
+  bool is_end_selection_visible = end_selection_.visible();
+
+  // When the view is only partially visible we need to check if
+  // the visible part contains selection rects.
+  auto visible_viewport_rect = GetVisibleViewportRect();
+  if (!visible_viewport_rect.IsEmpty() ||
+      rwhva_->offscreen_helper()->GetTopControlsHeight() > 0) {
+    auto view_offset =
+        rwhva_->offscreen_helper()->GetViewBoundsInPix().OffsetFromOrigin();
+    is_start_selection_visible =
+        is_start_selection_visible &&
+        !IsRectEmpty(IntersectRects(visible_viewport_rect, left + view_offset,
+                                    true /*ignore_width*/));
+    is_end_selection_visible =
+        is_end_selection_visible &&
+        !IsRectEmpty(IntersectRects(visible_viewport_rect, right + view_offset,
+                                    true /*ignore_width*/));
+  }
 
   // Is in edit field and no text is selected. show only single handle
   if (selection_data_->IsInEditField() && left == right) {
@@ -350,21 +374,21 @@ void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired(
 
     CHECK(start_selection_ == end_selection_);
 
-    if (GetCaretSelectionStatus() && is_selection_range_visible) {
-      gfx::Rect left = selection_data_->GetLeftRect();
+    if (GetCaretSelectionStatus() && is_start_selection_visible) {
       input_handle_->SetBasePosition(gfx::Point(left.x(), left.y()));
       input_handle_->Move(left.bottom_right());
       input_handle_->Show();
     }
 
     bool show_context_menu =
-        IsAnyHandleVisible() &&
+        input_handle_->IsVisible() &&
         effective_reason != Reason::ScrollOrZoomGestureEnded &&
         effective_reason != Reason::Tap &&
         effective_reason != Reason::Irrelevant;
-    if (!handle_being_dragged_ && show_context_menu)
+    if (!handle_being_dragged_ && show_context_menu) {
+      selection_change_reason_ = saved_reason;
       ShowContextMenu();
-
+    }
     return;
   }
   input_handle_->Hide();
@@ -384,54 +408,62 @@ void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired(
 
   start_handle_->SetBasePosition(left.bottom_left());
   start_handle->Move(left.bottom_left());
-  if (start_selection_.visible())
+  if (is_start_selection_visible)
     start_handle->Show();
   else
     start_handle->Hide();
 
   end_handle_->SetBasePosition(right.bottom_right());
   end_handle->Move(right.bottom_right());
-  if (end_selection_.visible())
+  if (is_end_selection_visible)
     end_handle->Show();
   else
     end_handle->Hide();
 
   // Do not show the context menu during selection extend and
   // offscreen selection.
-  if (!handle_being_dragged_ && effective_reason != Reason::Irrelevant &&
-      IsAnyHandleVisible()) {
+  if (!handle_being_dragged_ && selection_mode_ != SelectionMode::NONE) {
+    selection_change_reason_ = saved_reason;
     ShowContextMenu();
   }
 }
 
 void SelectionControllerEfl::ShowContextMenu() {
-#if !defined(USE_AURA)
-  content::ContextMenuParams convertedParams = *(selection_data_->GetContextMenuParams());
-
-  RenderWidgetHostViewEfl* rwhv =
-      static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
-  if (rwhv) {
-    int blinkX, blinkY;
-    rwhv->EvasToBlinkCords(convertedParams.x, convertedParams.y, &blinkX, &blinkY);
-    convertedParams.x = blinkX;
-    convertedParams.y = blinkY;
-
-    // TODO(a1.gomes): In case of EWK apps, the call below end up calling
-    // EWebView::ShowContextMenu. We have to make sure parameters
-    // are correct.
-    WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
-    WebContentsViewEfl* wcve = static_cast<WebContentsViewEfl*>(wci->GetView());
-    wcve->ShowContextMenu(convertedParams);
+  if (!rwhva_)
+    return;
+
+  if (IsMobileProfile()) {
+    if (context_menu_status_ == ContextMenuStatus::INPROGRESS)
+      return;
+    else
+      context_menu_status_ = ContextMenuStatus::INPROGRESS;
   }
-#endif
+  content::ContextMenuParams convertedParams =
+      *(selection_data_->GetContextMenuParams());
+
+  int blinkX, blinkY;
+  rwhva_->offscreen_helper()->EvasToBlinkCords(
+      convertedParams.x, convertedParams.y, &blinkX, &blinkY);
+  convertedParams.x = blinkX;
+  convertedParams.y = blinkY;
+
+  // TODO(a1.gomes): In case of EWK apps, the call below end up calling
+  // EWebView::ShowContextMenu. We have to make sure parameters
+  // are correct.
+  WebContentsImpl* wci = static_cast<WebContentsImpl*>(web_contents());
+  WebContentsViewAura* wcva = static_cast<WebContentsViewAura*>(wci->GetView());
+  wcva->wcva_helper()->ShowContextMenu(convertedParams);
 }
 
 void SelectionControllerEfl::CancelContextMenu(int request_id) {
-#if !defined(USE_AURA)
-  WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
-  WebContentsViewEfl* wcve = static_cast<WebContentsViewEfl*>(wci->GetView());
-  wcve->CancelContextMenu(request_id);
-#endif
+  if (IsMobileProfile() && (context_menu_status_ == ContextMenuStatus::HIDDEN ||
+                            context_menu_status_ == ContextMenuStatus::NONE)) {
+    return;
+  }
+
+  WebContentsImpl* wci = static_cast<WebContentsImpl*>(web_contents());
+  WebContentsViewAura* wcva = static_cast<WebContentsViewAura*>(wci->GetView());
+  wcva->wcva_helper()->CancelContextMenu(request_id);
 }
 
 void SelectionControllerEfl::HideHandles() {
@@ -593,11 +625,11 @@ void SelectionControllerEfl::HandleDragEndNotification() {
     ecore_event_filter_del(ecore_events_filter_);
   ecore_events_filter_ = nullptr;
 
-  selection_change_reason_ = Reason::Irrelevant;
+  selection_change_reason_ = Reason::HandleReleased;
   start_handle_->SetBasePosition(selection_data_->GetLeftRect().bottom_left());
   end_handle_->SetBasePosition(selection_data_->GetRightRect().bottom_right());
   handle_being_dragged_ = false;
-  ShowHandleAndContextMenuIfRequired(Reason::HandleReleased);
+  ShowHandleAndContextMenuIfRequired();
 }
 
 void SelectionControllerEfl::GetSelectionBounds(gfx::Rect* left,
@@ -649,14 +681,12 @@ void SelectionControllerEfl::HandlePostponedGesture(int x,
                                                     int y,
                                                     ui::EventType type) {
   DVLOG(0) << "HandlePostponedGesture :: " << type;
-#if !defined(USE_AURA)
-  RenderWidgetHostViewEfl* rwhv =
-      static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
+  RenderWidgetHostViewAura* rwhva = static_cast<RenderWidgetHostViewAura*>(
+      web_contents()->GetRenderWidgetHostView());
 
   gfx::Point point = gfx::Point(x, y);
-  if (rwhv)
-    point = rwhv->ConvertPointInViewPix(point);
-#endif
+  if (rwhva)
+    point = rwhva->offscreen_helper()->ConvertPointInViewPix(point);
   switch (type) {
     case ui::ET_GESTURE_LONG_PRESS: {
       ClearSelectionViaEWebView();
@@ -692,13 +722,12 @@ bool SelectionControllerEfl::HandleLongPressEvent(
     }
     SetSelectionStatus(true);
     SetSelectionEditable(true);
-    if (selection_mode_ == None)
-      SetSelectionMode(Caret);
+    if (selection_mode_ == SelectionMode::NONE)
+      SetSelectionMode(SelectionMode::CARET);
     HandleLongPressEventPrivate(touch_point);
     return true;
   } else if (params.link_url.is_empty() && params.src_url.is_empty() &&
-                 params.is_text_node ||
-             !params.selection_text.empty()) {
+             (params.is_text_node || !params.selection_text.empty())) {
     // If user is long pressing on a content with
     // -webkit-user-select: none, we should bail and not enter
     // selection neither show magnigier class or context menu.
@@ -791,7 +820,7 @@ void SelectionControllerEfl::ClearSelection() {
   CancelContextMenu(0);
   SetSelectionStatus(false);
   SetSelectionEditable(false);
-  SetSelectionMode(None);
+  SetSelectionMode(SelectionMode::NONE);
   selection_change_reason_ = Reason::Irrelevant;
 }
 
@@ -820,9 +849,7 @@ gfx::Rect SelectionControllerEfl::GetForbiddenRegionRect(
   left_rect.Offset(visible_viewport_rect.x(), visible_viewport_rect.y());
   right_rect.Offset(visible_viewport_rect.x(), visible_viewport_rect.y());
 
-  bool is_caret_mode =
-      selection_data_->IsInEditField() && left_rect == right_rect;
-  if (is_caret_mode) {
+  if (IsCaretMode()) {
     forbidden_region.set_x(left_rect.x());
     forbidden_region.set_y(input_handle_->IsTop()
                                ? left_rect.y() - kLargeHandleHeight
@@ -861,7 +888,7 @@ gfx::Rect SelectionControllerEfl::GetForbiddenRegionRect(
   forbidden_region.set_height(forbidden_region.height() + 2 * kMenuPadding);
 
   return IntersectRects(forbidden_region, visible_viewport_rect,
-                        is_caret_mode /*ignore_width*/);
+                        IsCaretMode() /*ignore_width*/);
 }
 
 gfx::Rect SelectionControllerEfl::GetVisibleViewportRect() const {
@@ -915,4 +942,23 @@ bool SelectionControllerEfl::ExistsSelectedText() {
   return selection_data_->IsInEditField() &&
          (selection_data_->GetLeftRect() != selection_data_->GetRightRect());
 }
+
+void SelectionControllerEfl::SetVisiblityStatus(bool visiblity) {
+  if (!visiblity && !handle_being_dragged_ && !is_caret_mode_forced_)
+    selection_change_reason_ = Reason::Irrelevant;
+  else if (selection_change_reason_ == Reason::Irrelevant)
+    selection_change_reason_ = Reason::RequestedByContextMenu;
+}
+
+void SelectionControllerEfl::ContextMenuStatusHidden() {
+  if (IsMobileProfile())
+    context_menu_status_ = ContextMenuStatus::HIDDEN;
+  SetVisiblityStatus(false);
+}
+
+void SelectionControllerEfl::ContextMenuStatusVisible() {
+  if (IsMobileProfile())
+    context_menu_status_ = ContextMenuStatus::VISIBLE;
+  SetVisiblityStatus(true);
+}
 }
index 8247b16..b41234c 100644 (file)
@@ -143,15 +143,22 @@ class CONTENT_EXPORT SelectionControllerEfl {
 
   bool ExistsSelectedText();
 
+  bool IsCaretMode() const;
+
+  void ContextMenuStatusHidden();
+  void ContextMenuStatusVisible();
+
  private:
-  enum SelectionMode {
-    None = 0,
-    Caret,
-    Range
-  };
+  enum class SelectionMode { NONE = 0, CARET, RANGE };
+
+  enum class ContextMenuStatus { NONE = 0, HIDDEN, INPROGRESS, VISIBLE };
+
   void DetermineSelectionMode(const gfx::Rect& left_rect, const gfx::Rect& right_rect);
   void SetSelectionMode(enum SelectionMode);
 
+  // Sets selection_change_reason_ based on visibility
+  void SetVisiblityStatus(bool visiblity);
+
   // To update the selection bounds
   // returns false if rects are invalid, otherwise true
   bool UpdateSelectionDataAndShow(const gfx::Rect& left_rect,
@@ -231,7 +238,8 @@ class CONTENT_EXPORT SelectionControllerEfl {
 
   SelectionHandleEfl* dragged_handle_ = nullptr;
 
-  enum SelectionMode selection_mode_;
+  enum ContextMenuStatus context_menu_status_ = ContextMenuStatus::NONE;
+  enum SelectionMode selection_mode_ = SelectionMode::NONE;
   gfx::SelectionBound start_selection_;
   gfx::SelectionBound end_selection_;
 };
index 21aea88..b85d8ac 100644 (file)
@@ -9,6 +9,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/strings/escape.h"
 #include "base/strings/utf_string_conversions.h"
 #include "browser_context_efl.h"
 #include "common/web_contents_utils.h"
@@ -30,6 +31,8 @@
 #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)
@@ -48,11 +51,15 @@ 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;
 
 const std::string kWebSearchLink("https://www.google.com/search?q=");
 const char* const kEditCommandUnselect = "Unselect";
+const char* const kDefaultImageExtension = "jpg";
 }
 
 namespace content {
@@ -62,12 +69,7 @@ std::vector<ContextMenuItemEfl> ContextMenuControllerEfl::_context_menu_listdata
 
 ContextMenuControllerEfl::ContextMenuControllerEfl(EWebView* wv,
                                                    WebContents& web_contents)
-    : webview_(wv),
-      popup_(nullptr),
-      menu_items_(nullptr),
-      web_contents_(web_contents),
-      is_text_selection_(false),
-      weak_ptr_factory_(this) {
+    : webview_(wv), web_contents_(web_contents), weak_ptr_factory_(this) {
   native_view_ =
       static_cast<WebContentsImpl*>(&web_contents_)->GetEflNativeView();
 }
@@ -183,6 +185,7 @@ void ContextMenuControllerEfl::GetProposedContextMenu() {
           blink::mojom::ContextMenuDataInputFieldType::kPassword) {
     AddItemToProposedList(EWK_CONTEXT_MENU_ITEM_TYPE_ACTION, EWK_CONTEXT_MENU_ITEM_TAG_COPY,
         std::string(dgettext("WebKit", "IDS_WEBVIEW_OPT_COPY")));
+
     if (!params_.is_editable) {
       AddItemToProposedList(
           EWK_CONTEXT_MENU_ITEM_TYPE_ACTION,
@@ -215,7 +218,7 @@ void ContextMenuControllerEfl::GetProposedContextMenu() {
         std::string(dgettext("WebKit", "IDS_WEBVIEW_OPT_COPY_IMAGE")),
         params_.src_url.spec(),
         params_.src_url.spec());
-  } else if (!params_.link_url.is_empty()) {
+  } else if (!params_.link_url.is_empty() && !params_.is_user_select_none) {
     AddItemToProposedList(EWK_CONTEXT_MENU_ITEM_TYPE_ACTION,
         EWK_CONTEXT_MENU_ITEM_TAG_TEXT_SELECTION_MODE,
         std::string(dgettext("WebKit", "IDS_WEBVIEW_OPT_SELECTION_MODE_ABB")),
@@ -284,22 +287,24 @@ bool ContextMenuControllerEfl::CreateContextMenu(
 #endif
     elm_object_tree_focus_allow_set(popup_, false);
   } else {
-    evas_object_data_set(native_view_, "ContextMenuContollerEfl", this);
-    popup_ = elm_popup_add(native_view_);
-    elm_popup_align_set(popup_, 0.5, 0.5);
+    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;
-  auto smart_parent = evas_object_smart_parent_get(native_view_);
-  if (!smart_parent) {
-    LOG(ERROR) << "Unable to get smart parent from native view";
-    evas_object_del(popup_);
-    popup_ = nullptr;
-    return false;
-  }
-  evas_object_smart_member_add(popup_, smart_parent);
-  elm_object_part_content_set(smart_parent, "context_menu_popup", popup_);
+
+  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);
 
@@ -338,40 +343,29 @@ bool ContextMenuControllerEfl::CreateContextMenu(
   } 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)) {
-        std::vector<std::string> link_split;
+      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;
-        while ((next_delimiter = selected_link.find(":", current_pos))
-               != std::string::npos) {
-          std::string part = selected_link.substr(current_pos,
-                                                  next_delimiter - current_pos);
-          link_split.push_back(part);
+        // 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);
-        if (!last_part.empty())
-          link_split.push_back(last_part);
-
-        if (!link_split[1].size()) {
-          std::vector<std::string> real_link_split;
-          size_t current_pos = 0;
-          size_t next_delimiter = 0;
-          while ((next_delimiter = link_split[1].find("?", current_pos)) != std::string::npos) {
-            std::string part = link_split[1].substr(current_pos, next_delimiter - current_pos);
-            real_link_split.push_back(part);
-            current_pos = next_delimiter + 1;
-          }
-          std::string last_part = link_split[1].substr(current_pos);
-          if (!last_part.empty())
-            real_link_split.push_back(last_part);
-
-          elm_object_part_text_set(popup_, "title,text", real_link_split[0].c_str());
+        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());
@@ -433,14 +427,53 @@ void ContextMenuControllerEfl::ContextMenuHWBackKey(void* data, Evas_Object* obj
   }
 }
 
+// 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())
+    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, "ContextEfl"));
-  if (menu_controller) {
-    menu_controller->HideContextMenu();
-    menu_controller->HideSelectionHandle();
-    evas_object_data_del(obj, "ContextEfl");
+  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,
@@ -465,16 +498,11 @@ void ContextMenuControllerEfl::RequestSelectionRect() const {
 }
 
 gfx::Point ContextMenuControllerEfl::CalculateSelectionMenuPosition(
-    gfx::Rect selection_rect) {
-  auto rwhva = static_cast<RenderWidgetHostViewAura*>(
-      web_contents_.GetRenderWidgetHostView());
-  auto selection_controller = webview_->GetSelectionController();
-  CHECK(rwhva);
-  CHECK(selection_controller);
-  auto webview_bounds = rwhva->offscreen_helper()->GetViewBoundsInPix();
+    const gfx::Rect& selection_rect) {
+  auto* selection_controller = webview_->GetSelectionController();
+  if (!selection_controller)
+    return gfx::Point();
 
-  // Consider a webview offset.
-  selection_rect.Offset(webview_bounds.x(), webview_bounds.y());
   auto visible_viewport_rect = selection_controller->GetVisibleViewportRect();
   auto forbidden_rect =
       selection_controller->GetForbiddenRegionRect(selection_rect);
@@ -482,36 +510,62 @@ gfx::Point ContextMenuControllerEfl::CalculateSelectionMenuPosition(
   // 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());
-    return position;
-  }
-  // If there is no sufficient space above, we're trying to place selection menu
-  // below forbidden region.
-  if (forbidden_rect.bottom() + kMenuHeight < visible_viewport_rect.bottom()) {
+  } 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);
-    return position;
+  } 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());
   }
-  // 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(
-    const gfx::Rect& selection_rect) {
-  gfx::Point selection_menu_position =
-      CalculateSelectionMenuPosition(selection_rect);
-  if (!webview_->GetSelectionController()->GetVisibleViewportRect().Contains(
-          selection_menu_position)) {
+    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);
-  web_contents_.Focus();
+
   if (IsMobileProfile())
     elm_ctxpopup_auto_hide_disabled_set(popup_, EINA_TRUE);
 
@@ -521,14 +575,22 @@ void ContextMenuControllerEfl::OnSelectionRectReceived(
 #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.
@@ -536,17 +598,18 @@ bool ContextMenuControllerEfl::ShowContextMenu() {
   } 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);
-    // Workaround. After context menu shows up, there is "unfocused" event fired.
-    // evas_object_smart_callback_add(popup_, "unfocused", ContextMenuHWBackKey, this);
-    evas_object_focus_set(popup_, EINA_TRUE);
+    eext_object_event_callback_add(popup_, EEXT_CALLBACK_MORE,
+                                   ContextMenuHWBackKey, this);
 #endif
     evas_object_show(popup_);
     evas_object_raise(popup_);
-    return true;
+    if (IsMobileProfile())
+      context_menu_status_ = VISIBLE;
+  }
+  return true;
 }
 
 void ContextMenuControllerEfl::HideSelectionHandle() {
@@ -576,6 +639,8 @@ void ContextMenuControllerEfl::RequestShowSelectionHandleAndContextMenu(
 void ContextMenuControllerEfl::OnClipboardDownload(
     download::DownloadItem* item,
     download::DownloadInterruptReason interrupt_reason) {
+  item->SetUserData(DownloadManagerDelegateEfl::kDownloadTemporaryFile,
+                    base::WrapUnique(new base::SupportsUserData::Data()));
   item->AddObserver(this);
   clipboard_download_items_.insert(item);
 }
@@ -601,14 +666,11 @@ void ContextMenuControllerEfl::OnDiskDownload(
 void ContextMenuControllerEfl::OnDownloadUpdated(
     download::DownloadItem* download) {
   if(download && download->AllDataSaved()) {
-    if (clipboard_download_items_.find(download) != clipboard_download_items_.end()) {
-#if !defined(WAYLAND_BRINGUP)
+    if (clipboard_download_items_.find(download) !=
+        clipboard_download_items_.end()) {
       const std::string& download_path = download->GetForcedFilePath().value();
       ClipboardHelperEfl::GetInstance()->SetData(download_path,
                                                  ClipboardDataTypeEfl::IMAGE);
-#else
-      NOTIMPLEMENTED();
-#endif
       download->RemoveObserver(this);
       clipboard_download_items_.erase(download);
     }
@@ -627,6 +689,7 @@ void ContextMenuControllerEfl::OnDownloadUpdated(
 base::FilePath ContextMenuControllerEfl::DownloadFile(
     const GURL url,
     const base::FilePath outputDir,
+    const base::FilePath suggested_filename,
     download::DownloadUrlParameters::OnStartedCallback callback) {
   LOG(INFO) << "Downloading file: " << url << "to: "<< outputDir.value();
   const GURL referrer = web_contents_.GetVisibleURL();
@@ -645,7 +708,11 @@ base::FilePath ContextMenuControllerEfl::DownloadFile(
   dl_params->set_referrer(referrer);
 #endif
   dl_params->set_referrer_encoding("utf8");
-  base::FilePath fileName = net::GenerateFileName(url,"","","","","");
+  base::FilePath fileName;
+  if (suggested_filename.empty())
+    fileName = net::GenerateFileName(url, "", "", "", "", "");
+  else
+    fileName = suggested_filename;
   base::FilePath fullPath = outputDir.Append(fileName);
 
   while (base::PathExists(fullPath)) {
@@ -706,6 +773,24 @@ 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;
+  app_control_create(&app_control);
+
+  app_control_set_operation(app_control, APP_CONTROL_OPERATION_SEARCH);
+  if (IsMobileProfile()) {
+    app_control_add_extra_data(
+        app_control, "http://tizen.org/appcontrol/data/keyword", text.c_str());
+  } else {
+    app_control_add_extra_data(app_control, APP_CONTROL_DATA_TEXT,
+                               text.c_str());
+  }
+  app_control_send_launch_request(app_control, NULL, NULL);
+
+  app_control_destroy(app_control);
+}
+
 void ContextMenuControllerEfl::LaunchShareApp(const std::string& text) {
   app_control_h handle = nullptr;
   int error_code = app_control_create(&handle);
@@ -803,8 +888,7 @@ void ContextMenuControllerEfl::MenuItemSelected(ContextMenuItemEfl* menu_item)
   if (!webview_)
     return;
 
-  if (popup_)
-    evas_object_hide(popup_);
+  HideContextMenu();
   // FIXME: Add cases as and when required
   switch(menu_item->GetContextMenuOption())
   {
@@ -863,20 +947,43 @@ void ContextMenuControllerEfl::MenuItemSelected(ContextMenuItemEfl* menu_item)
       break;
     }
     case EWK_CONTEXT_MENU_ITEM_TAG_COPY_IMAGE_TO_CLIPBOARD: {
-      LOG(INFO) << "[CLIPBOARD] copying image to clipboard!";
-      std::string plain_text = params_.src_url.spec();
-      ClipboardHelperEfl::GetInstance()->SetData(
-          plain_text, ClipboardDataTypeEfl::PLAIN_TEXT);
+      // FIXME(g.ludwikowsk): Find the correct path for temporarily saving
+      // images to clipboard.
+      base::FilePath copy_image_dir("/tmp/");
+      base::FilePath suggested_filename;
+      if (params_.src_url.has_query()) {
+        suggested_filename = base::FilePath(base::UnescapeURLComponent(
+            params_.src_url.query(),
+            base::UnescapeRule::SPACES | base::UnescapeRule::PATH_SEPARATORS |
+                base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS));
+      } else {
+        suggested_filename = net::GenerateFileName(GURL(params_.src_url.spec()),
+                                                   "", "", "", "", "");
+      }
 
-      // FIXME(g.ludwikowsk): Setting markup data doesn't work like this,
-      // selection_text doesn't contain html. Do we actually need to do this?
-      ClipboardHelperEfl::GetInstance()->SetData(
-          base::UTF16ToASCII(params_.selection_text),
-          ClipboardDataTypeEfl::MARKUP);
-      DownloadFile(
-          GURL(params_.src_url.spec()), base::FilePath("/tmp/"),
-          base::BindRepeating(&ContextMenuControllerEfl::OnClipboardDownload,
-                              weak_ptr_factory_.GetWeakPtr()));
+      if (suggested_filename.Extension().empty()) {
+        suggested_filename =
+            suggested_filename.AddExtension(kDefaultImageExtension);
+      }
+
+      base::FilePath fullPath = copy_image_dir.Append(suggested_filename);
+      // For files with the same name clipboard app refuses to add another one,
+      // saying that it is already copied to clipboard. When DownloadFile
+      // tries to download a file which already exists, it appends a number
+      // to the file name. This causes clipboard app to recognize it as a
+      // different file and clipboard is filled with duplicate images.
+      // To prevent this, we do not download file if a file with the same name
+      // already exists. We still call SetData, so clipboard app would show
+      // toast popup for duplicates. This is for Tizen 2.4 compatibility.
+      if (PathExists(fullPath)) {
+        ClipboardHelperEfl::GetInstance()->SetData(fullPath.value(),
+                                                   ClipboardDataTypeEfl::IMAGE);
+      } else {
+        DownloadFile(
+            GURL(params_.src_url.spec()), copy_image_dir, suggested_filename,
+            base::BindRepeating(&ContextMenuControllerEfl::OnClipboardDownload,
+                                weak_ptr_factory_.GetWeakPtr()));
+      }
       break;
     }
     case EWK_CONTEXT_MENU_ITEM_TAG_COPY_LINK_DATA: {
@@ -896,7 +1003,7 @@ void ContextMenuControllerEfl::MenuItemSelected(ContextMenuItemEfl* menu_item)
           LOG(ERROR) << "Could not get downloads directory.";
           break;
         }
-        DownloadFile(GURL(params_.link_url.spec()), path,
+        DownloadFile(GURL(params_.link_url.spec()), path, base::FilePath(),
                      base::BindOnce(&ContextMenuControllerEfl::OnDiskDownload,
                                     weak_ptr_factory_.GetWeakPtr()));
       }
@@ -909,7 +1016,7 @@ void ContextMenuControllerEfl::MenuItemSelected(ContextMenuItemEfl* menu_item)
           LOG(ERROR) << "Could not get image downloads directory.";
           break;
         }
-        DownloadFile(GURL(params_.src_url.spec()), path,
+        DownloadFile(GURL(params_.src_url.spec()), path, base::FilePath(),
                      base::BindOnce(&ContextMenuControllerEfl::OnDiskDownload,
                                     weak_ptr_factory_.GetWeakPtr()));
       }
@@ -948,8 +1055,17 @@ void ContextMenuControllerEfl::MenuItemSelected(ContextMenuItemEfl* menu_item)
     case EWK_CONTEXT_MENU_ITEM_TAG_SEARCH_WEB: {
       std::string search_text = base::UTF16ToUTF8(params_.selection_text);
       webview_->ClearSelection();
+#if BUILDFLAG(IS_TIZEN)
+      if (IsMobileProfile()) {
+        LaunchBrowserWithWebSearch(search_text);
+      } else {
+        OpenURL(GURL(kWebSearchLink + search_text),
+                WindowOpenDisposition::NEW_FOREGROUND_TAB);
+      }
+#else
       OpenURL(GURL(kWebSearchLink + search_text),
               WindowOpenDisposition::NEW_FOREGROUND_TAB);
+#endif
       break;
     }
     case EWK_CONTEXT_MENU_ITEM_TAG_CLIPBOARD: {
@@ -976,6 +1092,16 @@ 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);
@@ -983,14 +1109,17 @@ void ContextMenuControllerEfl::HideContextMenu() {
     popup_ = nullptr;
   }
 
+  if (restore_timer_) {
+    ecore_timer_del(restore_timer_);
+    restore_timer_ = nullptr;
+  }
+
   if (IsMobileProfile()) {
-#if !defined(EWK_BRINGUP)
     context_menu_status_ = HIDDEN;
-#endif
   } else if (menu_items_) {
     void* data;
     EINA_LIST_FREE(menu_items_, data) {
-      _Ewk_Context_Menu_Item *item = static_cast<_Ewk_Context_Menu_Item*> (data);
+      _Ewk_Context_Menu_Item* item = static_cast<_Ewk_Context_Menu_Item*>(data);
       delete item;
     }
     menu_items_ = nullptr;
@@ -1008,6 +1137,9 @@ void ContextMenuControllerEfl::Resize(const gfx::Rect& webview_rect) {
   if (!popup_)
     return;
 
+  if (IsMobileProfile() && context_menu_status_ != VISIBLE)
+    return;
+
   if (is_text_selection_) {
     RequestSelectionRect();
   } else {
@@ -1016,12 +1148,19 @@ void ContextMenuControllerEfl::Resize(const gfx::Rect& webview_rect) {
 }
 
 void ContextMenuControllerEfl::DeletePopup() {
-  if (native_view_)
-    evas_object_data_set(native_view_, "ContextMenuContollerEfl", 0);
+  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);
-    evas_object_smart_member_del(popup_);
+    if (!IsMobileProfile())
+      evas_object_smart_member_del(popup_);
     evas_object_del(popup_);
     popup_ = nullptr;
     list_ = nullptr;
@@ -1035,5 +1174,7 @@ void ContextMenuControllerEfl::DeletePopup() {
     }
     menu_items_ = nullptr;
   }
+  if (IsMobileProfile())
+    context_menu_status_ = NONE;
 }
 }
index eb17afe..1c898d2 100644 (file)
@@ -82,14 +82,15 @@ class ContextMenuControllerEfl : public download::DownloadItem::Observer {
   void Resize(const gfx::Rect& webview_rect);
 
   void RequestSelectionRect() const;
-  void OnSelectionRectReceived(const gfx::Rect& selection_rect);
-  gfx::Point CalculateSelectionMenuPosition(gfx::Rect selection_rect);
+  void OnSelectionRectReceived(gfx::Rect selection_rect);
+  gfx::Point CalculateSelectionMenuPosition(const gfx::Rect& selection_rect);
 
  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();
   void CustomMenuItemSelected(ContextMenuItemEfl*);
@@ -114,30 +115,38 @@ class ContextMenuControllerEfl : public download::DownloadItem::Observer {
   base::FilePath DownloadFile(
       const GURL url,
       const base::FilePath outputDir,
+      const base::FilePath suggested_filename,
       download::DownloadUrlParameters::OnStartedCallback callback);
   bool TriggerDownloadCb(const GURL url);
   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* popup_;
+  Evas_Object* top_widget_ = nullptr;
+  Evas_Object* popup_ = nullptr;
   Evas_Object* list_ = nullptr;
-  Eina_List* menu_items_;
+  Eina_List* menu_items_ = nullptr;
   ContextMenuParams params_;
   WebContents& web_contents_;
   gfx::Point context_menu_show_pos_;
-  bool is_text_selection_;
+  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_;
 };
 
index 74c01a6..ba15e89 100644 (file)
@@ -14,6 +14,9 @@
 #include "ewk/efl_integration/eweb_view.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 
+const void* const DownloadManagerDelegateEfl::kDownloadTemporaryFile =
+    &DownloadManagerDelegateEfl::kDownloadTemporaryFile;
+
 DownloadManagerDelegateEfl::DownloadManagerDelegateEfl(
     content::DownloadManager* download_manager)
     : download_manager_(download_manager) {}
@@ -58,6 +61,9 @@ base::FilePath DownloadManagerDelegateEfl::GetPlatformDownloadPath(
 bool DownloadManagerDelegateEfl::DetermineDownloadTarget(
     download::DownloadItem* item,
     content::DownloadTargetCallback* callback) {
+  if (item->GetUserData(kDownloadTemporaryFile))
+    return false;
+
   if (TriggerExternalDownloadManager(item)) {
     std::move(*callback).Run(
         base::FilePath(), /* Empty file path for cancellation */
index fc9141c..11c6c9f 100644 (file)
@@ -31,6 +31,8 @@ public:
                          content::DownloadOpenDelayedCallback) override;
  void GetNextId(content::DownloadIdCallback) override;
 
+ static const void* const kDownloadTemporaryFile;
+
 private:
  // If the external download manager is available, trigger download
  // with it and return true, otherwise return false.
index a9a6708..40f93c3 100644 (file)
@@ -122,6 +122,8 @@ const char* kCustomScrollBeginSignalName = "custom,scroll,begin";
 // email-app specific signal which informs that custom scrolling is finished.
 const char* kCustomScrollEndSignalName = "custom,scroll,end";
 
+const float kDelayShowContextMenuTime = 0.2f;
+
 inline void SetDefaultStringIfNull(const char*& variable,
                                    const char* default_string) {
   if (!variable) {
@@ -1290,11 +1292,37 @@ void EWebView::UpdateContextMenuWithParams(
     const content::ContextMenuParams& params) {
   context_menu_.reset(
       new content::ContextMenuControllerEfl(this, *web_contents_.get()));
-  if (!context_menu_->PopulateAndShowContextMenu(params)) {
-    context_menu_.reset();
-    if (GetSelectionController())
-      GetSelectionController()->HideHandles();
+
+  if (IsMobileProfile()) {
+    if (delayed_show_context_menu_timer_) {
+      ecore_timer_del(delayed_show_context_menu_timer_);
+      delayed_show_context_menu_timer_ = nullptr;
+    }
+    saved_context_menu_params_ = params;
+    delayed_show_context_menu_timer_ = ecore_timer_add(
+        kDelayShowContextMenuTime, DelayedPopulateAndShowContextMenu, this);
+  } else {
+    if (!context_menu_->PopulateAndShowContextMenu(params)) {
+      context_menu_.reset();
+      if (GetSelectionController())
+        GetSelectionController()->HideHandles();
+    }
+  }
+}
+
+Eina_Bool EWebView::DelayedPopulateAndShowContextMenu(void* data) {
+  if (IsMobileProfile) {
+    EWebView* view = static_cast<EWebView*>(data);
+    if (view) {
+      if (view->context_menu_ &&
+          !(view->context_menu_->PopulateAndShowContextMenu(
+              view->saved_context_menu_params_))) {
+        view->context_menu_.reset();
+      }
+      view->delayed_show_context_menu_timer_ = nullptr;
+    }
   }
+  return ECORE_CALLBACK_CANCEL;
 }
 
 void EWebView::CancelContextMenu(int request_id) {
index 601562e..42f4698 100644 (file)
@@ -738,6 +738,8 @@ class EWebView {
                                         void* event_info);
   void UpdateContextMenuWithParams(const content::ContextMenuParams& params);
 
+  static Eina_Bool DelayedPopulateAndShowContextMenu(void* data);
+
   scoped_refptr<WebViewEvasEventHandler> evas_event_handler_;
   scoped_refptr<Ewk_Context> context_;
   std::unique_ptr<content::WebContents> web_contents_;
@@ -877,6 +879,8 @@ class EWebView {
 #if defined(TIZEN_VIDEO_HOLE)
   bool pending_video_hole_setting_ = false;
 #endif
+
+  Ecore_Timer* delayed_show_context_menu_timer_ = nullptr;
 };
 
 const unsigned int g_default_tilt_motion_sensitivity = 3;