Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / web_contents / touch_editable_impl_aura.cc
1 // Copyright (c) 2013 The Chromium Authors. 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 "content/browser/web_contents/touch_editable_impl_aura.h"
6
7 #include "content/browser/renderer_host/render_widget_host_impl.h"
8 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/common/view_messages.h"
11 #include "content/public/browser/render_view_host.h"
12 #include "content/public/browser/render_widget_host.h"
13 #include "grit/ui_strings.h"
14 #include "ui/aura/client/screen_position_client.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_tree_host.h"
17 #include "ui/base/clipboard/clipboard.h"
18 #include "ui/base/ui_base_switches_util.h"
19 #include "ui/gfx/range/range.h"
20 #include "ui/wm/public/activation_client.h"
21
22 namespace content {
23
24 ////////////////////////////////////////////////////////////////////////////////
25 // TouchEditableImplAura, public:
26
27 TouchEditableImplAura::~TouchEditableImplAura() {
28   Cleanup();
29 }
30
31 // static
32 TouchEditableImplAura* TouchEditableImplAura::Create() {
33   if (switches::IsTouchEditingEnabled())
34     return new TouchEditableImplAura();
35   return NULL;
36 }
37
38 void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura* view) {
39   if (rwhva_ == view)
40     return;
41
42   Cleanup();
43   if (!view)
44     return;
45
46   rwhva_ = view;
47   rwhva_->set_touch_editing_client(this);
48 }
49
50 void TouchEditableImplAura::UpdateEditingController() {
51   if (!rwhva_ || !rwhva_->HasFocus())
52     return;
53
54   if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE ||
55       selection_anchor_rect_ != selection_focus_rect_) {
56     if (touch_selection_controller_)
57       touch_selection_controller_->SelectionChanged();
58   } else {
59     EndTouchEditing(false);
60   }
61 }
62
63 void TouchEditableImplAura::OverscrollStarted() {
64   overscroll_in_progress_ = true;
65 }
66
67 void TouchEditableImplAura::OverscrollCompleted() {
68   // We might receive multiple OverscrollStarted() and OverscrollCompleted()
69   // during the same scroll session (for example, when the scroll direction
70   // changes). We want to show the handles only when:
71   // 1. Overscroll has completed
72   // 2. Scrolling session is over, i.e. we have received ET_GESTURE_SCROLL_END.
73   // 3. If we had hidden the handles when scrolling started
74   // 4. If there is still a need to show handles (there is a non-empty selection
75   // or non-NONE |text_input_type_|)
76   if (overscroll_in_progress_ && !scroll_in_progress_ &&
77       handles_hidden_due_to_scroll_ &&
78       (selection_anchor_rect_ != selection_focus_rect_ ||
79           text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) {
80     StartTouchEditing();
81     UpdateEditingController();
82   }
83   overscroll_in_progress_ = false;
84 }
85
86 ////////////////////////////////////////////////////////////////////////////////
87 // TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient
88 // implementation:
89
90 void TouchEditableImplAura::StartTouchEditing() {
91   if (!rwhva_ || !rwhva_->HasFocus())
92     return;
93
94   if (!touch_selection_controller_) {
95     touch_selection_controller_.reset(
96         ui::TouchSelectionController::create(this));
97   }
98   if (touch_selection_controller_)
99     touch_selection_controller_->SelectionChanged();
100 }
101
102 void TouchEditableImplAura::EndTouchEditing(bool quick) {
103   if (touch_selection_controller_) {
104     if (touch_selection_controller_->IsHandleDragInProgress()) {
105       touch_selection_controller_->SelectionChanged();
106     } else {
107       touch_selection_controller_->HideHandles(quick);
108       touch_selection_controller_.reset();
109     }
110   }
111 }
112
113 void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor,
114                                                        const gfx::Rect& focus) {
115   selection_anchor_rect_ = anchor;
116   selection_focus_rect_ = focus;
117
118   // If touch editing handles were not visible, we bring them up only if
119   // there is non-zero selection on the page. And the current event is a
120   // gesture event (we dont want to show handles if the user is selecting
121   // using mouse or keyboard).
122   if (selection_gesture_in_process_ && !scroll_in_progress_ &&
123       !overscroll_in_progress_ &&
124       selection_anchor_rect_ != selection_focus_rect_) {
125     StartTouchEditing();
126     selection_gesture_in_process_ = false;
127   }
128
129   UpdateEditingController();
130 }
131
132 void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) {
133   text_input_type_ = type;
134 }
135
136 bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
137   DCHECK(rwhva_);
138   if (event->IsTouchEvent())
139     return false;
140
141   if (!event->IsGestureEvent()) {
142     selection_gesture_in_process_ = false;
143     EndTouchEditing(false);
144     return false;
145   }
146
147   const ui::GestureEvent* gesture_event =
148       static_cast<const ui::GestureEvent*>(event);
149   switch (event->type()) {
150     case ui::ET_GESTURE_TAP:
151       if (gesture_event->details().tap_count() > 1)
152         selection_gesture_in_process_ = true;
153       // When the user taps, we want to show touch editing handles if user
154       // tapped on selected text.
155       if (selection_anchor_rect_ != selection_focus_rect_) {
156         // UnionRects only works for rects with non-zero width.
157         gfx::Rect anchor(selection_anchor_rect_.origin(),
158                          gfx::Size(1, selection_anchor_rect_.height()));
159         gfx::Rect focus(selection_focus_rect_.origin(),
160                         gfx::Size(1, selection_focus_rect_.height()));
161         gfx::Rect selection_rect = gfx::UnionRects(anchor, focus);
162         if (selection_rect.Contains(gesture_event->location())) {
163           StartTouchEditing();
164           return true;
165         }
166       }
167       // For single taps, not inside selected region, we want to show handles
168       // only when the tap is on an already focused textfield.
169       textfield_was_focused_on_tap_ = false;
170       if (gesture_event->details().tap_count() == 1 &&
171           text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)
172         textfield_was_focused_on_tap_ = true;
173       break;
174     case ui::ET_GESTURE_LONG_PRESS:
175       selection_gesture_in_process_ = true;
176       break;
177     case ui::ET_GESTURE_SCROLL_BEGIN:
178       // If selection handles are currently visible, we want to get them back up
179       // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that
180       // we can re-start touch editing on scroll end gesture.
181       scroll_in_progress_ = true;
182       handles_hidden_due_to_scroll_ = false;
183       if (touch_selection_controller_)
184         handles_hidden_due_to_scroll_ = true;
185       EndTouchEditing(true);
186       break;
187     case ui::ET_GESTURE_SCROLL_END:
188       // Scroll has ended, but we might still be in overscroll animation.
189       if (handles_hidden_due_to_scroll_ && !overscroll_in_progress_ &&
190           (selection_anchor_rect_ != selection_focus_rect_ ||
191               text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) {
192         StartTouchEditing();
193         UpdateEditingController();
194       }
195       // fall through to reset |scroll_in_progress_|.
196     case ui::ET_SCROLL_FLING_START:
197       selection_gesture_in_process_ = false;
198       scroll_in_progress_ = false;
199       break;
200     default:
201       break;
202   }
203   return false;
204 }
205
206 void TouchEditableImplAura::GestureEventAck(int gesture_event_type) {
207   DCHECK(rwhva_);
208   if (gesture_event_type == blink::WebInputEvent::GestureTap &&
209       text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
210       textfield_was_focused_on_tap_) {
211     StartTouchEditing();
212     UpdateEditingController();
213   }
214 }
215
216 void TouchEditableImplAura::OnViewDestroyed() {
217   Cleanup();
218 }
219
220 ////////////////////////////////////////////////////////////////////////////////
221 // TouchEditableImplAura, ui::TouchEditable implementation:
222
223 void TouchEditableImplAura::SelectRect(const gfx::Point& start,
224                                        const gfx::Point& end) {
225   RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
226   RenderViewHost* rvh = RenderViewHost::From(host);
227   WebContentsImpl* wc =
228       static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(rvh));
229   wc->SelectRange(start, end);
230 }
231
232 void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) {
233   if (!rwhva_)
234     return;
235
236   RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
237       rwhva_->GetRenderWidgetHost());
238   host->MoveCaret(point);
239 }
240
241 void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1,
242                                                   gfx::Rect* p2) {
243   *p1 = selection_anchor_rect_;
244   *p2 = selection_focus_rect_;
245 }
246
247 gfx::Rect TouchEditableImplAura::GetBounds() {
248   return rwhva_ ? gfx::Rect(rwhva_->GetNativeView()->bounds().size()) :
249       gfx::Rect();
250 }
251
252 gfx::NativeView TouchEditableImplAura::GetNativeView() const {
253   return rwhva_ ? rwhva_->GetNativeView()->GetToplevelWindow() : NULL;
254 }
255
256 void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) {
257   if (!rwhva_)
258     return;
259   aura::Window* window = rwhva_->GetNativeView();
260   aura::client::ScreenPositionClient* screen_position_client =
261       aura::client::GetScreenPositionClient(window->GetRootWindow());
262   if (screen_position_client)
263     screen_position_client->ConvertPointToScreen(window, point);
264 }
265
266 void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) {
267   if (!rwhva_)
268     return;
269   aura::Window* window = rwhva_->GetNativeView();
270   aura::client::ScreenPositionClient* screen_position_client =
271       aura::client::GetScreenPositionClient(window->GetRootWindow());
272   if (screen_position_client)
273     screen_position_client->ConvertPointFromScreen(window, point);
274 }
275
276 bool TouchEditableImplAura::DrawsHandles() {
277   return false;
278 }
279
280 void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) {
281   if (!rwhva_)
282     return;
283   gfx::Point point = anchor;
284   ConvertPointFromScreen(&point);
285   RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
286   host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), point));
287   EndTouchEditing(false);
288 }
289
290 bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const {
291   NOTREACHED();
292   return false;
293 }
294
295 bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const {
296   if (!rwhva_)
297     return false;
298   bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
299   gfx::Range selection_range;
300   rwhva_->GetSelectionRange(&selection_range);
301   bool has_selection = !selection_range.is_empty();
302   switch (command_id) {
303     case IDS_APP_CUT:
304       return editable && has_selection;
305     case IDS_APP_COPY:
306       return has_selection;
307     case IDS_APP_PASTE: {
308       base::string16 result;
309       ui::Clipboard::GetForCurrentThread()->ReadText(
310           ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
311       return editable && !result.empty();
312     }
313     case IDS_APP_DELETE:
314       return editable && has_selection;
315     case IDS_APP_SELECT_ALL:
316       return true;
317     default:
318       return false;
319   }
320 }
321
322 bool TouchEditableImplAura::GetAcceleratorForCommandId(
323     int command_id,
324     ui::Accelerator* accelerator) {
325   return false;
326 }
327
328 void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
329   RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
330   RenderViewHost* rvh = RenderViewHost::From(host);
331   WebContents* wc = WebContents::FromRenderViewHost(rvh);
332
333   switch (command_id) {
334     case IDS_APP_CUT:
335       wc->Cut();
336       break;
337     case IDS_APP_COPY:
338       wc->Copy();
339       break;
340     case IDS_APP_PASTE:
341       wc->Paste();
342       break;
343     case IDS_APP_DELETE:
344       wc->Delete();
345       break;
346     case IDS_APP_SELECT_ALL:
347       wc->SelectAll();
348       break;
349     default:
350       NOTREACHED();
351       break;
352   }
353   EndTouchEditing(false);
354 }
355
356 void TouchEditableImplAura::DestroyTouchSelection() {
357   EndTouchEditing(false);
358 }
359
360 ////////////////////////////////////////////////////////////////////////////////
361 // TouchEditableImplAura, private:
362
363 TouchEditableImplAura::TouchEditableImplAura()
364     : text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
365       rwhva_(NULL),
366       selection_gesture_in_process_(false),
367       handles_hidden_due_to_scroll_(false),
368       scroll_in_progress_(false),
369       overscroll_in_progress_(false),
370       textfield_was_focused_on_tap_(false) {
371 }
372
373 void TouchEditableImplAura::Cleanup() {
374   if (rwhva_) {
375     rwhva_->set_touch_editing_client(NULL);
376     rwhva_ = NULL;
377   }
378   text_input_type_ = ui::TEXT_INPUT_TYPE_NONE;
379   EndTouchEditing(true);
380   selection_gesture_in_process_ = false;
381   handles_hidden_due_to_scroll_ = false;
382   scroll_in_progress_ = false;
383   overscroll_in_progress_ = false;
384 }
385
386 }  // namespace content