[M108 Migration][Text Selection] Selection handles & Caret Selection
[platform/framework/web/chromium-efl.git] / tizen_src / chromium_impl / content / browser / selection / selection_controller_efl.cc
1 // Copyright 2013 Samsung Electronics. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "selection_controller_efl.h"
6
7 #include <Elementary.h>
8
9 #include "base/trace_event/trace_event.h"
10 #include "content/browser/renderer_host/render_view_host_impl.h"
11 #include "content/browser/renderer_host/render_widget_host_impl.h"
12 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
13 #include "content/browser/web_contents/web_contents_impl.h"
14 #include "content/browser/web_contents/web_contents_impl_efl.h"
15 #include "content/public/browser/context_menu_params.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "ui/base/clipboard/clipboard_helper_efl.h"
19 #include "ui/display/screen.h"
20 #include "ui/gfx/geometry/dip_util.h"
21
22 namespace content {
23
24 static const int menuHeight = 140;// The Height fo the context menu.
25 static const int menuPadding = 60;// This is padding for deciding when to modify context menu position.
26 static const int spacePadding = 0; // 24;// This is for making context menu closer to the handles.
27
28 bool IsRectEmpty(const gfx::Rect& rect) {
29   return rect == gfx::Rect();
30 }
31
32 gfx::Vector2dF ComputeLineOffsetFromBottom(const gfx::SelectionBound& bound) {
33   gfx::Vector2dF line_offset =
34       gfx::ScaleVector2d(bound.edge_start() - bound.edge_end(), 0.5f);
35   // An offset of 8 DIPs is sufficient for most line sizes. For small lines,
36   // using half the line height avoids synthesizing a point on a line above
37   // (or below) the intended line.
38   const gfx::Vector2dF kMaxLineOffset(8.f, 8.f);
39   line_offset.SetToMin(kMaxLineOffset);
40   line_offset.SetToMax(-kMaxLineOffset);
41   return line_offset;
42 }
43
44 content::WebContents* SelectionControllerEfl::web_contents() const {
45   return rwhva_->offscreen_helper()->GetWebContents();
46 }
47
48 SelectionControllerEfl::SelectionControllerEfl(RenderWidgetHostViewAura* rwhva)
49     : rwhva_(rwhva),
50       selection_data_(new SelectionBoxEfl(rwhva)),
51       start_handle_(
52           new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_LEFT)),
53       end_handle_(
54           new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_RIGHT)),
55       input_handle_(
56           new SelectionHandleEfl(*this, SelectionHandleEfl::HANDLE_TYPE_INPUT)),
57       magnifier_(new SelectionMagnifierEfl(this)),
58       selection_mode_(None) {
59   evas_object_event_callback_add(rwhva_->offscreen_helper()->content_image(),
60                                  EVAS_CALLBACK_MOVE,
61                                  &EvasParentViewMoveCallback, this);
62 #if BUILDFLAG(IS_TIZEN)
63   vconf_notify_key_changed(VCONFKEY_LANGSET, PlatformLanguageChanged, this);
64 #endif
65 }
66
67 SelectionControllerEfl::~SelectionControllerEfl() {
68   if (ecore_events_filter_)
69     ecore_event_filter_del(ecore_events_filter_);
70   if (GetSelectionStatus())
71     ClearSelectionViaEWebView();
72   HideHandleAndContextMenu();
73
74   evas_object_event_callback_del(rwhva_->offscreen_helper()->content_image(),
75                                  EVAS_CALLBACK_MOVE,
76                                  &EvasParentViewMoveCallback);
77 }
78
79 void SelectionControllerEfl::SetSelectionStatus(bool enable) {
80   TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__, "enable", enable);
81   selection_data_->SetStatus(enable);
82 }
83
84 bool SelectionControllerEfl::GetSelectionStatus() const {
85   TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
86                "status", selection_data_->GetStatus());
87   return selection_data_->GetStatus();
88 }
89
90 #if BUILDFLAG(IS_TIZEN)
91 void SelectionControllerEfl::PlatformLanguageChanged(keynode_t* keynode, void* data) {
92   SelectionControllerEfl* selection_controller = static_cast<SelectionControllerEfl*>(data);
93   if (!selection_controller)
94     return;
95
96   if (selection_controller->GetSelectionStatus())
97     selection_controller->ClearSelectionViaEWebView();
98   selection_controller->HideHandleAndContextMenu();
99 }
100 #endif
101
102 void SelectionControllerEfl::SetSelectionEditable(bool enable) {
103   TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__, "enable", enable);
104   selection_data_->SetEditable(enable);
105 }
106
107 bool SelectionControllerEfl::GetSelectionEditable() const {
108   TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
109                "editable", selection_data_->GetEditable());
110   return selection_data_->GetEditable();
111 }
112
113 bool SelectionControllerEfl::GetCaretSelectionStatus() const {
114   TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
115                "caret selection", selection_mode_ == Caret);
116   return selection_mode_ == Caret;
117 }
118
119 void SelectionControllerEfl::SetControlsTemporarilyHidden(bool value) {
120   TRACE_EVENT1("selection,efl", __PRETTY_FUNCTION__,
121                "controls are hidden:", value);
122   if (controls_temporarily_hidden_ == value)
123     return;
124   // Make sure to show selection controls only when no finger
125   // is left touching the screen.
126   if (!value && rwhva_->event_handler()->pointer_state().GetPointerCount())
127     return;
128   controls_temporarily_hidden_ = value;
129   if (value) {
130     Clear();
131     CancelContextMenu(0);
132   } else {
133     ShowHandleAndContextMenuIfRequired(Reason::ScrollOrZoomGestureEnded);
134   }
135 }
136
137 void SelectionControllerEfl::SetIsAnchorFirst(bool value) {
138   selection_data_->SetIsAnchorFirst(value);
139 }
140
141 void SelectionControllerEfl::OnSelectionChanged(
142     const gfx::SelectionBound& start, const gfx::SelectionBound& end) {
143
144   if (start_selection_ == start && end_selection_ == end)
145     return;
146
147   start_selection_ = start;
148   end_selection_ = end;
149
150   gfx::Rect truncated_start(start.edge_start_rounded(),
151                             gfx::Size(0, start.GetHeight()));
152   gfx::Rect truncated_end(end.edge_start_rounded(),
153                           gfx::Size(0, end.GetHeight()));
154
155   static float device_scale_factor =
156       display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
157   truncated_start = gfx::ToEnclosingRect(
158       gfx::ConvertRectToPixels(truncated_start, device_scale_factor));
159   truncated_end = gfx::ToEnclosingRect(
160       gfx::ConvertRectToPixels(truncated_end, device_scale_factor));
161
162   if (handle_being_dragged_ &&
163       dragging_handle_->Type() != SelectionHandleEfl::HANDLE_TYPE_INPUT) {
164     dragging_handle_->MoveObject(selection_data_->GetIsAnchorFirst()
165                                      ? truncated_end.bottom_right()
166                                      : truncated_start.bottom_left());
167   }
168
169   bool finger_down = handle_being_dragged_ || long_mouse_press_;
170   bool show = (selection_change_reason_ != Reason::Irrelevant) && !finger_down;
171   UpdateSelectionDataAndShow(
172       truncated_start, truncated_end, show);
173   selection_change_reason_ = Reason::Irrelevant;
174
175   // In case of the selected text contains only line break and no other
176   // characters, we should use caret selection mode.
177   if (GetSelectionEditable() && !handle_being_dragged_ &&
178       rwhva_->GetSelectedText() == (u"\n")) {
179     rwhva_->offscreen_helper()->MoveCaret(
180         selection_data_->GetLeftRect().origin());
181     is_caret_mode_forced_ = true;
182   }
183 }
184
185 void SelectionControllerEfl::OnTextInputStateChanged() {
186   // In order to confirm if a long press gesture is ongoing,
187   // we just needed to check "long_mouse_press_".
188   // However, when long press happens on a link or image,
189   // long_mouse_press_ is set to false in ::HandleLongPressEvent,
190   // although finger is still down.
191   // To compensate this, add an extra check asking from the engine
192   // if a finger is still touching down.
193   bool is_touch_down =
194       rwhva_->event_handler()->pointer_state().GetPointerCount() > 0;
195
196   // If on an editable field and in caret selection mode, only
197   // show the large handle if:
198   // 1) we are dragging the insertion handle around;
199   // 2) we are expecting an "update" from the engine, e.g.
200   //    handling "tap" gesture.
201   bool finger_down = handle_being_dragged_ || long_mouse_press_ || is_touch_down;
202
203   if (GetSelectionEditable() && GetCaretSelectionStatus() && !finger_down &&
204       !controls_temporarily_hidden_ &&
205       selection_change_reason_ == Reason::Irrelevant) {
206     HideHandleAndContextMenu();
207     ClearSelection();
208   }
209 }
210
211 void SelectionControllerEfl::UpdateSelectionData(const std::u16string& text) {
212   selection_data_->UpdateSelectStringData(text);
213 }
214
215 bool SelectionControllerEfl::ClearSelectionViaEWebView() {
216   if (!GetSelectionStatus() || !web_contents()->GetRenderViewHost() ||
217       web_contents()->IsBeingDestroyed()) {
218     return false;
219   }
220
221   RenderWidgetHostDelegate* host_delegate =
222       RenderWidgetHostImpl::From(
223           web_contents()->GetRenderViewHost()->GetWidget())
224           ->delegate();
225   if (host_delegate) {
226     host_delegate->ExecuteEditCommand("Unselect", absl::nullopt);
227     return true;
228   }
229   return false;
230 }
231
232 void SelectionControllerEfl::SetSelectionMode(enum SelectionMode mode) {
233   selection_mode_ = mode;
234 }
235
236 void SelectionControllerEfl::ToggleCaretAfterSelection() {
237   if (!GetCaretSelectionStatus() && GetSelectionEditable()) {
238     rwhva_->offscreen_helper()->MoveCaret(
239         selection_data_->GetRightRect().origin());
240     ClearSelection();
241   }
242 }
243
244 void SelectionControllerEfl::DetermineSelectionMode(
245     const gfx::Rect& left_rect,
246     const gfx::Rect& right_rect) {
247
248   if (left_rect == gfx::Rect() && right_rect == gfx::Rect())
249     SetSelectionMode(None);
250   else if (left_rect == right_rect && GetSelectionEditable())
251     SetSelectionMode(Caret);
252   else
253     SetSelectionMode(Range);
254 }
255
256 bool SelectionControllerEfl::UpdateSelectionDataAndShow(
257     const gfx::Rect& left_rect,
258     const gfx::Rect& right_rect,
259     bool /* show */) {
260
261   DetermineSelectionMode(left_rect, right_rect);
262
263   TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
264   if ((selection_change_reason_ == Reason::Irrelevant) && !IsSelectionValid(left_rect, right_rect)) {
265     if (!GetCaretSelectionStatus())
266       ClearSelection();
267     selection_data_->UpdateRectData(left_rect, right_rect);
268     return false;
269   }
270
271   if (selection_on_empty_form_control_ &&
272       (!ClipboardHelperEfl::GetInstance()->CanPasteFromSystemClipboard() ||
273        selection_change_reason_ == Reason::Tap)) {
274     ClearSelection();
275     return false;
276   }
277
278   if (selection_data_->UpdateRectData(left_rect, right_rect))
279     ShowHandleAndContextMenuIfRequired();
280
281   return true;
282 }
283
284 void SelectionControllerEfl::ShowHandleAndContextMenuIfRequired(
285     Reason explicit_reason) {
286   TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
287
288   Reason effective_reason = selection_change_reason_;
289   if (explicit_reason != Reason::Irrelevant)
290     effective_reason = explicit_reason;
291
292   // TODO(a1.gomes): Is not in selection mode, maybe we should not be this far
293   // to begin with.
294   if (!selection_data_->GetStatus())
295     return;
296
297   if (controls_temporarily_hidden_ || long_mouse_press_)
298     return;
299
300   gfx::Rect left, right;
301   left = selection_data_->GetLeftRect();
302   right = selection_data_->GetRightRect();
303
304   if (left.x() == 0 && left.y() == 0 && right.x() == 0 && right.y() == 0) {
305     selection_data_->ClearRectData();
306     return;
307   }
308
309   bool is_selection_range_visible =
310       start_selection_.visible() || end_selection_.visible();
311
312   // Is in edit field and no text is selected. show only single handle
313   if (selection_data_->IsInEditField() && left == right) {
314     if (!GetSelectionEditable())
315       return;
316
317     CHECK(start_selection_ == end_selection_);
318
319     if (GetCaretSelectionStatus() && is_selection_range_visible) {
320       gfx::Rect left = selection_data_->GetLeftRect();
321       input_handle_->SetBasePosition(gfx::Point(left.x(), left.y()));
322       input_handle_->Move(left.bottom_right());
323       input_handle_->Show();
324     }
325     start_handle_->Hide();
326     end_handle_->Hide();
327
328     bool show_context_menu =
329         IsAnyHandleVisible() &&
330         effective_reason != Reason::ScrollOrZoomGestureEnded &&
331         effective_reason != Reason::Tap &&
332         effective_reason != Reason::Irrelevant;
333     if (!handle_being_dragged_ && show_context_menu)
334       ShowContextMenu();
335
336     return;
337   }
338   input_handle_->Hide();
339
340   SelectionHandleEfl* start_handle = start_handle_.get();
341   SelectionHandleEfl* end_handle = end_handle_.get();
342   if (handle_being_dragged_) {
343     bool is_anchor_first = selection_data_->GetIsAnchorFirst();
344     if (is_anchor_first) {
345       start_handle = stationary_handle_;
346       end_handle = dragging_handle_;
347     } else {
348       start_handle = dragging_handle_;
349       end_handle = stationary_handle_;
350     }
351   }
352
353   start_handle_->SetBasePosition(left.bottom_left());
354   start_handle->Move(left.bottom_left());
355   if (start_selection_.visible())
356     start_handle->Show();
357   else
358     start_handle->Hide();
359
360   end_handle_->SetBasePosition(right.bottom_right());
361   end_handle->Move(right.bottom_right());
362   if (end_selection_.visible())
363     end_handle->Show();
364   else
365     end_handle->Hide();
366
367   // Do not show the context menu during selection extend and
368   // offscreen selection.
369   if (!handle_being_dragged_ && effective_reason != Reason::Irrelevant &&
370       IsAnyHandleVisible()) {
371     ShowContextMenu();
372   }
373 }
374
375 void SelectionControllerEfl::ShowContextMenu() {
376 #if !defined(USE_AURA)
377   content::ContextMenuParams convertedParams = *(selection_data_->GetContextMenuParams());
378
379   RenderWidgetHostViewEfl* rwhv =
380       static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
381   if (rwhv) {
382     int blinkX, blinkY;
383     rwhv->EvasToBlinkCords(convertedParams.x, convertedParams.y, &blinkX, &blinkY);
384     convertedParams.x = blinkX;
385     convertedParams.y = blinkY;
386
387     // TODO(a1.gomes): In case of EWK apps, the call below end up calling
388     // EWebView::ShowContextMenu. We have to make sure parameters
389     // are correct.
390     WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
391     WebContentsViewEfl* wcve = static_cast<WebContentsViewEfl*>(wci->GetView());
392     wcve->ShowContextMenu(convertedParams);
393   }
394 #endif
395 }
396
397 void SelectionControllerEfl::CancelContextMenu(int request_id) {
398 #if !defined(USE_AURA)
399   WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
400   WebContentsViewEfl* wcve = static_cast<WebContentsViewEfl*>(wci->GetView());
401   wcve->CancelContextMenu(request_id);
402 #endif
403 }
404
405 void SelectionControllerEfl::HideHandles() {
406   Clear();
407 }
408
409 void SelectionControllerEfl::HideHandleAndContextMenu() {
410   CancelContextMenu(0);
411   HideHandles();
412 }
413
414 bool SelectionControllerEfl::IsAnyHandleVisible() const {
415   return (start_handle_->IsVisible() ||
416           end_handle_->IsVisible() ||
417           input_handle_->IsVisible());
418 }
419
420 void SelectionControllerEfl::Clear() {
421   start_handle_->Hide();
422   end_handle_->Hide();
423   input_handle_->Hide();
424 }
425
426 Eina_Bool SelectionControllerEfl::EcoreEventFilterCallback(void* user_data,
427                                                            void* /*loop_data*/,
428                                                            int type,
429                                                            void* event_info) {
430   if (type == ECORE_EVENT_MOUSE_MOVE) {
431     auto controller = static_cast<SelectionControllerEfl*>(user_data);
432     if (!controller || !controller->dragged_handle_)
433       return ECORE_CALLBACK_PASS_ON;
434
435     auto event = static_cast<Ecore_Event_Mouse_Move*>(event_info);
436     controller->dragged_handle_->UpdatePosition(gfx::Point(event->x, event->y));
437     return ECORE_CALLBACK_CANCEL;
438   }
439
440   return ECORE_CALLBACK_PASS_ON;
441 }
442
443 void SelectionControllerEfl::HandleDragBeginNotification(
444     SelectionHandleEfl* handle) {
445   selection_change_reason_ = Reason::HandleDragged;
446   dragged_handle_ = handle;
447   ecore_events_filter_ =
448       ecore_event_filter_add(nullptr, EcoreEventFilterCallback, nullptr, this);
449
450   if (!ecore_events_filter_) {
451     LOG(ERROR) << __PRETTY_FUNCTION__
452                << " : Unable to create ecore events filter";
453   }
454
455   // Hide context menu on mouse down
456   CancelContextMenu(0);
457
458   handle_being_dragged_ = true;
459
460   Evas_Coord x, y;
461   evas_object_geometry_get(rwhva_->offscreen_helper()->content_image(), &x, &y,
462                            0, 0);
463   gfx::Point magnifier_point(
464       handle->GetBasePosition().x() + x,
465       handle->GetBasePosition().y() + y);
466
467   magnifier_->UpdateLocation(magnifier_point);
468   magnifier_->Move(magnifier_point);
469   magnifier_->Show();
470
471   if (handle == input_handle_.get())
472     return;
473
474   gfx::Vector2dF base_offset, extent_offset;
475
476   // When moving the handle we want to move only the extent point.
477   // Before doing so, we must make sure that the base point is set correctly.
478   if (handle == start_handle_.get()) {
479     dragging_handle_ = start_handle_.get();
480     stationary_handle_ = end_handle_.get();
481
482     base_offset = ComputeLineOffsetFromBottom(end_selection_);
483     extent_offset = ComputeLineOffsetFromBottom(start_selection_);
484   } else {
485     dragging_handle_ = end_handle_.get();
486     stationary_handle_ = start_handle_.get();
487
488     base_offset = ComputeLineOffsetFromBottom(start_selection_);
489     extent_offset = ComputeLineOffsetFromBottom(end_selection_);
490   }
491
492   float device_scale_factor =
493       display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
494   gfx::PointF base = gfx::ScalePoint(
495       gfx::PointF(stationary_handle_->GetBasePosition()), 1 / device_scale_factor);
496   gfx::PointF extent = gfx::ScalePoint(
497       gfx::PointF(dragging_handle_->GetBasePosition()), 1 / device_scale_factor);
498
499   base += base_offset;
500   extent += extent_offset;
501
502   WebContentsImpl* wci = static_cast<WebContentsImpl*>(web_contents());
503   wci->SelectRange(gfx::ToFlooredPoint(base), gfx::ToFlooredPoint(extent));
504 }
505
506 void SelectionControllerEfl::HandleDragUpdateNotification(SelectionHandleEfl* handle) {
507   if (!rwhva_)
508     return;
509
510   // FIXME : Check the text Direction later
511   gfx::Rect view_bounds = rwhva_->offscreen_helper()->GetViewBoundsInPix();
512   selection_change_reason_ = Reason::HandleDragged;
513
514   gfx::Point magnifier_point;
515   magnifier_point.set_x(handle->GetBasePosition().x() + view_bounds.x());
516   magnifier_point.set_y(handle->GetBasePosition().y() + view_bounds.y());
517   magnifier_->UpdateLocation(magnifier_point);
518   magnifier_->Move(magnifier_point);
519
520   switch (handle->Type()) {
521     case SelectionHandleEfl::HANDLE_TYPE_INPUT: {
522       rwhva_->offscreen_helper()->MoveCaret(handle->GetBasePosition());
523       return;
524     }
525     case SelectionHandleEfl::HANDLE_TYPE_LEFT:
526     case SelectionHandleEfl::HANDLE_TYPE_RIGHT: {
527       float device_scale_factor =
528           display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor();
529       gfx::PointF extent = gfx::ScalePoint(
530           gfx::PointF(handle->GetBasePosition()), 1 / device_scale_factor);
531
532       bool is_anchor_first = selection_data_->GetIsAnchorFirst();
533       gfx::Vector2dF line_offset = is_anchor_first
534                                    ? ComputeLineOffsetFromBottom(start_selection_)
535                                    : ComputeLineOffsetFromBottom(end_selection_);
536       extent += line_offset;
537
538       WebContentsImpl* wci = static_cast<WebContentsImpl*>(web_contents());
539       wci->MoveRangeSelectionExtent(gfx::ToFlooredPoint(extent));
540       return;
541     }
542   }
543 }
544
545 void SelectionControllerEfl::HandleDragEndNotification() {
546   dragged_handle_ = nullptr;
547
548   if (ecore_events_filter_)
549     ecore_event_filter_del(ecore_events_filter_);
550   ecore_events_filter_ = nullptr;
551
552   selection_change_reason_ = Reason::Irrelevant;
553   magnifier_->Hide();
554   start_handle_->SetBasePosition(selection_data_->GetLeftRect().bottom_left());
555   end_handle_->SetBasePosition(selection_data_->GetRightRect().bottom_right());
556   handle_being_dragged_ = false;
557   ShowHandleAndContextMenuIfRequired(Reason::HandleReleased);
558 }
559
560 void SelectionControllerEfl::GetSelectionBounds(gfx::Rect* left,
561                                                 gfx::Rect* right) {
562   if (left)
563     *left = selection_data_->GetLeftRect();
564   if (right)
565     *right = selection_data_->GetRightRect();
566 }
567
568 void SelectionControllerEfl::HandleGesture(blink::WebGestureEvent& event) {
569   blink::WebInputEvent::Type event_type = event.GetType();
570 #if !defined(EWK_BRINGUP)  // FIXME: m67 bringup
571   if (event_type == blink::WebInputEvent::kGestureTap) {
572     HandlePostponedGesture(event.x, event.y, ui::ET_GESTURE_TAP);
573   } else if (event_type == blink::WebInputEvent::kGestureShowPress) {
574     HandlePostponedGesture(
575         event.x, event.y, ui::ET_GESTURE_SHOW_PRESS);
576   } else if (event_type == blink::WebInputEvent::kGestureLongPress) {
577     HandlePostponedGesture(
578         event.x, event.y, ui::ET_GESTURE_LONG_PRESS);
579     long_mouse_press_ = true;
580   }
581 #endif
582   if (event_type == blink::WebInputEvent::Type::kGestureScrollBegin ||
583       event_type == blink::WebInputEvent::Type::kGesturePinchBegin) {
584     SetControlsTemporarilyHidden(true);
585   } else if (event_type == blink::WebInputEvent::Type::kGestureScrollEnd ||
586              event_type == blink::WebInputEvent::Type::kGesturePinchEnd) {
587     SetControlsTemporarilyHidden(false);
588   }
589 }
590
591 void SelectionControllerEfl::HandlePostponedGesture(int x,
592                                                     int y,
593                                                     ui::EventType type) {
594   DVLOG(0) << "HandlePostponedGesture :: " << type;
595 #if !defined(USE_AURA)
596   RenderWidgetHostViewEfl* rwhv =
597       static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
598
599   gfx::Point point = gfx::Point(x, y);
600   if (rwhv)
601     point = rwhv->ConvertPointInViewPix(point);
602 #endif
603   switch (type) {
604     case ui::ET_GESTURE_LONG_PRESS: {
605       ClearSelectionViaEWebView();
606       HideHandleAndContextMenu();
607       // Long press data will call to WebContentsViewDelegateEwk.
608       // It is called by the chain that handles FrameHostMsg_ContextMenu.
609       break;
610     }
611     case ui::ET_GESTURE_TAP:
612     case ui::ET_GESTURE_SHOW_PRESS: {
613       // Do not do anything.
614       break;
615     }
616     default:
617       ClearSelectionViaEWebView();
618       break;
619   }
620 }
621
622 bool SelectionControllerEfl::HandleLongPressEvent(
623     const gfx::Point& touch_point,
624     const content::ContextMenuParams& params) {
625
626   if (params.is_editable) {
627     // If one long press on an empty form, do not enter selection mode.
628     // Instead, context menu will be shown if needed for clipboard actions.
629 #if !defined(USE_AURA)
630     RenderWidgetHostViewEfl* rwhv =
631         static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
632     if (params.selection_text.empty() &&
633         !(rwhv && !rwhv->IsLastAvailableTextEmpty()) &&
634         !ClipboardHelperEfl::GetInstance()->CanPasteFromSystemClipboard()) {
635       long_mouse_press_ = false;
636       return false;
637     }
638 #endif
639     SetSelectionStatus(true);
640     SetSelectionEditable(true);
641     if (selection_mode_ == None)
642       SetSelectionMode(Caret);
643     HandleLongPressEventPrivate(touch_point);
644     return true;
645   } else if (params.link_url.is_empty() && params.src_url.is_empty() &&
646                  params.is_text_node ||
647              !params.selection_text.empty()) {
648     // If user is long pressing on a content with
649     // -webkit-user-select: none, we should bail and not enter
650     // selection neither show magnigier class or context menu.
651     if (params.selection_text.empty()) {
652       long_mouse_press_ = false;
653       return false;
654     }
655     SetSelectionStatus(true);
656     SetSelectionEditable(false);
657     HandleLongPressEventPrivate(touch_point);
658     DVLOG(1) << __PRETTY_FUNCTION__ << ":: !link, !image, !media, text";
659     return true;
660   } else if (!params.src_url.is_empty() && params.has_image_contents) {
661     DVLOG(1) << __PRETTY_FUNCTION__ << ":: IMAGE";
662     long_mouse_press_ = false;
663     return false;
664   } else if (!params.link_url.is_empty()) {
665     DVLOG(1) << __PRETTY_FUNCTION__ << ":: LINK";
666     long_mouse_press_ = false;
667     return false;
668   }
669   // Default return is true, because if 'params' did not
670   // fall through any of the switch cases above, no further
671   // action should be taken by the callee.
672   return true;
673 }
674
675 void SelectionControllerEfl::HandleLongPressEventPrivate(
676     const gfx::Point& touch_point) {
677   Clear();
678
679   gfx::Rect view_bounds = rwhva_->offscreen_helper()->GetViewBoundsInPix();
680   magnifier_->HandleLongPress(gfx::Point(touch_point.x() + view_bounds.x(),
681                                          touch_point.y() + view_bounds.y()));
682 }
683
684 void SelectionControllerEfl::HandleLongPressMoveEvent(const gfx::Point& touch_point) {
685   if (rwhva_)
686     rwhva_->offscreen_helper()->SelectClosestWord(touch_point);
687 }
688
689 void SelectionControllerEfl::HandleLongPressEndEvent() {
690   long_mouse_press_ = false;
691   selection_change_reason_ = Reason::Irrelevant;
692   ShowHandleAndContextMenuIfRequired(Reason::LongPressEnded);
693 }
694
695 void SelectionControllerEfl::PostHandleTapGesture(bool is_content_editable) {
696   if (is_content_editable) {
697     DVLOG(1) << "PostHandleTapGesture :: Editable";
698     SetSelectionStatus(true);
699     SetSelectionEditable(true);
700     selection_change_reason_ = Reason::Tap;
701   } else {
702     SetSelectionEditable(false);
703   }
704 }
705
706 bool SelectionControllerEfl::IsSelectionValid(const gfx::Rect& left_rect,
707                                               const gfx::Rect& right_rect) {
708   TRACE_EVENT2("selection,efl", __PRETTY_FUNCTION__,
709                "left_rect", left_rect.ToString(),
710                "right_rect", right_rect.ToString());
711   // For all normal cases the widht will be 0 and we want to check empty which Implies
712   // x, y, h w all to be 0
713   if ((IsRectEmpty(left_rect) || IsRectEmpty(right_rect))) {
714     SetSelectionStatus(false);
715     return false;
716   }
717
718   // The most of sites return width of each rects as 0 when text is selected.
719   // However, some websites that have viewport attributes on meta tag v
720   // return width 0 right after they are loaded, even though text is not selected.
721   // Thus the width is not sufficient for checking selection condition.
722   // Further invesitigation showed left_rect and right_rect always have the same x,y values
723   // for such cases. So, the equality for x and y rather than width should be tested.
724   if (left_rect.x() == right_rect.x() && left_rect.y() == right_rect.y() &&
725       !selection_data_->IsInEditField()  && !handle_being_dragged_ &&
726       selection_change_reason_ == Reason::Irrelevant) {
727     SetSelectionStatus(false);
728     return false;
729   }
730
731   return true;
732 }
733
734 void SelectionControllerEfl::ClearSelection() {
735   TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
736   Clear();
737   CancelContextMenu(0);
738   SetSelectionStatus(false);
739   SetSelectionEditable(false);
740   SetSelectionMode(None);
741 }
742
743 void SelectionControllerEfl::OnParentParentViewMove() {
744   TRACE_EVENT0("selection,efl", __PRETTY_FUNCTION__);
745   start_handle_->Move(start_handle_->GetBasePosition());
746   end_handle_->Move(end_handle_->GetBasePosition());
747 }
748
749 gfx::Rect SelectionControllerEfl::GetLeftRect() {
750   return selection_data_->GetLeftRect();
751 }
752
753 gfx::Rect SelectionControllerEfl::GetRightRect() {
754   return selection_data_->GetRightRect();
755 }
756
757 void SelectionControllerEfl::ChangeContextMenuPosition(gfx::Point& position, int& drawDirection) {
758   drawDirection = DirectionNone; // Giving default Direction.
759   int handleHeight, webViewX, webViewY, webViewWidth, webViewHeight;
760   gfx::Rect imeRect;
761   edje_object_part_geometry_get(input_handle_->evas_object(), "handle", 0, 0, 0, &handleHeight);
762   evas_object_geometry_get(rwhva_->offscreen_helper()->content_image(),
763                            &webViewX, &webViewY, &webViewWidth, &webViewHeight);
764   gfx::Rect viewportRect = gfx::Rect(webViewX, webViewY, webViewWidth, webViewHeight);
765 #if !defined(USE_AURA)
766   RenderWidgetHostViewEfl* rwhv =
767       static_cast<RenderWidgetHostViewEfl*>(web_contents_.GetRenderWidgetHostView());
768   if (rwhv && rwhv->IsIMEShow()) { // Get the Visible Rect .
769     imeRect = rwhv->GetIMERect();
770     if ((viewportRect.y() + viewportRect.height()) > imeRect.y())
771         viewportRect.set_height(imeRect.y() - viewportRect.y());
772   }
773 #endif
774   gfx::Rect leftHandleRect = selection_data_->GetLeftRect();
775   leftHandleRect.set_x(webViewX + leftHandleRect.x());
776   leftHandleRect.set_y(webViewY + leftHandleRect.y());
777
778   if (!GetCaretSelectionStatus()) {
779     gfx::Rect rightHandleRect = selection_data_->GetRightRect();
780     rightHandleRect.set_x(webViewX + rightHandleRect.x());
781     rightHandleRect.set_y(webViewY + rightHandleRect.y());
782     gfx::Rect oldLeftHandleRect = leftHandleRect;
783     gfx::Rect oldRightHandleRect = rightHandleRect;
784     gfx::Rect effectiveRect;
785     bool isEffecrtiveRectAvailable = false;
786
787     bool isTop = false;
788     bool isRightHandle = false;
789     bool doConsiderRightHandle = false;
790     bool doNotConsiderMenuUpward = false;
791
792     //if (leftHandleRect.x() < 0)
793     //    leftHandleRect.set_x(0);
794     //if (leftHandleRect.y() < 0)
795     //    leftHandleRect.set_y(0);
796
797     // Giving first preference to left handle.
798     if (leftHandleRect.IsEmpty() && viewportRect.Contains(leftHandleRect.x(), leftHandleRect.y())) {
799       isTop = start_handle_->IsTop();
800       effectiveRect = leftHandleRect;
801       isEffecrtiveRectAvailable = true;
802       // Check whether Menu will overlap the right handle or not.
803       if (leftHandleRect != rightHandleRect) {
804         if (!isTop) {
805           // If there is sufficient space above the handler that Menu can be placed. then shift context menu
806           // then no need to change effectiveRect from leftHandleRect to rightHandleRect.
807           bool directionUp = effectiveRect.y() - menuHeight > viewportRect.y() && (effectiveRect.y() - (viewportRect.y() + menuHeight) > menuPadding);
808           if (!directionUp && ((leftHandleRect.y() + leftHandleRect.height()) + menuHeight) > rightHandleRect.y()) {
809             doConsiderRightHandle = true;
810             doNotConsiderMenuUpward = true; // As if we draw the direction is UP it will overlap with left Handle.
811           }
812         }
813       }
814     } else {
815       doConsiderRightHandle = true;
816     }
817
818     if (doConsiderRightHandle && rightHandleRect.IsEmpty() && viewportRect.Contains(rightHandleRect.x(), rightHandleRect.y())) {
819       effectiveRect = rightHandleRect;
820       isEffecrtiveRectAvailable = true;
821       isTop = end_handle_->IsTop();
822       isRightHandle = true;
823     }
824
825     if (isEffecrtiveRectAvailable) {
826       //We will go for UpWard, DownWard, Left, Right directions.
827       if (effectiveRect.y() - menuHeight > viewportRect.y() && (effectiveRect.y() - (viewportRect.y() + menuHeight) > menuPadding) && !doNotConsiderMenuUpward) {
828         if (isTop) {
829           if (isRightHandle) {
830             position.set_y(effectiveRect.y() - spacePadding - handleHeight);
831           } else {
832             position.set_y(effectiveRect.y() - spacePadding - handleHeight);
833             //position.set_y(position.y() - effectiveRect.height());
834           }
835         } else {
836           position.set_y(effectiveRect.y() - spacePadding);
837         }
838         drawDirection = DirectionUp;
839       } else if ((effectiveRect.y() + effectiveRect.height()) + menuHeight < (viewportRect.y() + viewportRect.height())) {
840         position.set_y((effectiveRect.y() + effectiveRect.height()) - spacePadding + handleHeight);
841         drawDirection = DirectionDown;
842       } else if (effectiveRect.x() < (viewportRect.x() + viewportRect.width()/2)) {
843         position.set_x((effectiveRect.x() + effectiveRect.width()) + spacePadding + handleHeight);
844         position.set_y(effectiveRect.CenterPoint().y());
845         drawDirection = DirectionLeft;
846       } else {
847         position.set_x(effectiveRect.x());
848         position.set_y(effectiveRect.CenterPoint().y());
849         drawDirection = DirectionRight;
850       }
851     } else {
852       if (oldLeftHandleRect.y() < viewportRect.y() && oldRightHandleRect.y() > (viewportRect.y() + viewportRect.height()) && viewportRect.height() <= webViewHeight) {
853         position.set_y(viewportRect.CenterPoint().y() - spacePadding);
854       } else {
855         position.set_y(position.y() + spacePadding + handleHeight);// This value is chosen based on the how much close the context menu arrow should come to word.
856       }
857       drawDirection = DirectionNone;
858     }
859
860     return;
861   }
862
863   if (!selection_data_->IsInEditField())
864     return;
865
866   position.set_y(leftHandleRect.y() - spacePadding);
867
868   if (input_handle_->IsTop()) {
869     position.set_y(position.y() - leftHandleRect.height() - handleHeight);
870     return;
871   }
872
873   // The Width of the context menu is menuHeight so comapairing current position with the viewport point plus menuHeight.
874   // If position is less than this value then set new position for the contextmenu.
875   if ((position.y() <= (webViewY + menuHeight)) || ((position.y() - (webViewY + menuHeight)) <= menuPadding)) {
876     if (leftHandleRect.IsEmpty()) {
877       position.set_y(position.y() + spacePadding + handleHeight);// This value is chosen based on the how much close the context menu arrow should come to word.
878       drawDirection = DirectionDown;
879      return;
880     }
881
882     position.set_y((leftHandleRect.y() + leftHandleRect.height()) - spacePadding + handleHeight);
883     drawDirection = DirectionDown;
884     return;
885   }
886
887   return;
888 }
889
890 gfx::Rect SelectionControllerEfl::GetVisibleViewportRect() const {
891   if (custom_visible_view_rect_.IsEmpty())
892     return rwhva_->offscreen_helper()->GetViewBoundsInPix();
893
894   return gfx::IntersectRects(rwhva_->offscreen_helper()->GetViewBoundsInPix(),
895                              custom_visible_view_rect_);
896 }
897
898 bool SelectionControllerEfl::TextSelectionDown(int x, int y) {
899   /*
900    * According to webkit-efl textSelectionDown is used on long press gesture, we already
901    * have implementation for handling this gesture in SelectionControllerEfl so we just
902    * fallback into it. Although I'm not totally sure that this is expected behaviour as there
903    * is no clear explanation what should this API do.
904    *
905    * Reference from webkit-efl:
906    * Source/WebKit2/UIProcess/API/efl/ewk_view.cpp line 614
907    */
908   if (!long_mouse_press_) {
909     long_mouse_press_ = true;
910     HandleLongPressEventPrivate(gfx::Point(x, y));
911     return true;
912   }
913
914   return false;
915 }
916
917 bool SelectionControllerEfl::TextSelectionUp(int /*x*/, int /*y*/) {
918   /*
919    * According to webkit-efl textSelectionUp is used when MouseUp event occurs. We already
920    * have implementation for handling MouseUp after long press in SelectionMagnifierEfl so we just
921    * fallback into it. Although I'm not totally sure that this is expected behaviour as there
922    * is no clear explanation what should this API do.
923    *
924    * Reference from webkit-efl:
925    * Source/WebKit2/UIProcess/API/efl/tizen/TextSelection.cpp line 807
926    */
927
928   if (long_mouse_press_) {
929     magnifier_->OnAnimatorUp();
930     return true;
931   }
932
933   return false;
934 }
935
936 }