Upstream version 9.38.198.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       selection_gesture_in_process_ = false;
108       touch_selection_controller_->HideHandles(quick);
109       touch_selection_controller_.reset();
110     }
111   }
112 }
113
114 void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor,
115                                                        const gfx::Rect& focus) {
116   selection_anchor_rect_ = anchor;
117   selection_focus_rect_ = focus;
118
119   // If touch editing handles were not visible, we bring them up only if
120   // there is non-zero selection on the page. And the current event is a
121   // gesture event (we dont want to show handles if the user is selecting
122   // using mouse or keyboard).
123   if (selection_gesture_in_process_ && !scroll_in_progress_ &&
124       !overscroll_in_progress_ &&
125       selection_anchor_rect_ != selection_focus_rect_) {
126     StartTouchEditing();
127     selection_gesture_in_process_ = false;
128   }
129
130   UpdateEditingController();
131 }
132
133 void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) {
134   text_input_type_ = type;
135 }
136
137 bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
138   DCHECK(rwhva_);
139   if (!event->IsGestureEvent()) {
140     // Ignore all non-gesture events. Non-gesture events that can deactivate
141     // touch editing are handled in TouchSelectionControllerImpl.
142     return false;
143   }
144
145   const ui::GestureEvent* gesture_event =
146       static_cast<const ui::GestureEvent*>(event);
147   switch (event->type()) {
148     case ui::ET_GESTURE_TAP:
149       // When the user taps, we want to show touch editing handles if user
150       // tapped on selected text.
151       if (gesture_event->details().tap_count() == 1 &&
152           selection_anchor_rect_ != selection_focus_rect_) {
153         // UnionRects only works for rects with non-zero width.
154         gfx::Rect anchor(selection_anchor_rect_.origin(),
155                          gfx::Size(1, selection_anchor_rect_.height()));
156         gfx::Rect focus(selection_focus_rect_.origin(),
157                         gfx::Size(1, selection_focus_rect_.height()));
158         gfx::Rect selection_rect = gfx::UnionRects(anchor, focus);
159         if (selection_rect.Contains(gesture_event->location())) {
160           StartTouchEditing();
161           return true;
162         }
163       }
164       // For single taps, not inside selected region, we want to show handles
165       // only when the tap is on an already focused textfield.
166       textfield_was_focused_on_tap_ = false;
167       if (gesture_event->details().tap_count() == 1 &&
168           text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)
169         textfield_was_focused_on_tap_ = true;
170       break;
171     case ui::ET_GESTURE_LONG_PRESS:
172       selection_gesture_in_process_ = true;
173       break;
174     case ui::ET_GESTURE_SCROLL_BEGIN:
175       // If selection handles are currently visible, we want to get them back up
176       // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that
177       // we can re-start touch editing on scroll end gesture.
178       scroll_in_progress_ = true;
179       handles_hidden_due_to_scroll_ = false;
180       if (touch_selection_controller_)
181         handles_hidden_due_to_scroll_ = true;
182       EndTouchEditing(true);
183       break;
184     case ui::ET_GESTURE_SCROLL_END:
185       // Scroll has ended, but we might still be in overscroll animation.
186       if (handles_hidden_due_to_scroll_ && !overscroll_in_progress_ &&
187           (selection_anchor_rect_ != selection_focus_rect_ ||
188               text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) {
189         StartTouchEditing();
190         UpdateEditingController();
191       }
192       // fall through to reset |scroll_in_progress_|.
193     case ui::ET_SCROLL_FLING_START:
194       selection_gesture_in_process_ = false;
195       scroll_in_progress_ = false;
196       break;
197     default:
198       break;
199   }
200   return false;
201 }
202
203 void TouchEditableImplAura::GestureEventAck(int gesture_event_type) {
204   DCHECK(rwhva_);
205   if (gesture_event_type == blink::WebInputEvent::GestureTap &&
206       text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
207       textfield_was_focused_on_tap_) {
208     StartTouchEditing();
209     UpdateEditingController();
210   }
211 }
212
213 void TouchEditableImplAura::OnViewDestroyed() {
214   Cleanup();
215 }
216
217 ////////////////////////////////////////////////////////////////////////////////
218 // TouchEditableImplAura, ui::TouchEditable implementation:
219
220 void TouchEditableImplAura::SelectRect(const gfx::Point& start,
221                                        const gfx::Point& end) {
222   RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
223   RenderViewHost* rvh = RenderViewHost::From(host);
224   WebContentsImpl* wc =
225       static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(rvh));
226   wc->SelectRange(start, end);
227 }
228
229 void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) {
230   if (!rwhva_)
231     return;
232
233   RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
234       rwhva_->GetRenderWidgetHost());
235   host->MoveCaret(point);
236 }
237
238 void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1,
239                                                   gfx::Rect* p2) {
240   *p1 = selection_anchor_rect_;
241   *p2 = selection_focus_rect_;
242 }
243
244 gfx::Rect TouchEditableImplAura::GetBounds() {
245   return rwhva_ ? gfx::Rect(rwhva_->GetNativeView()->bounds().size()) :
246       gfx::Rect();
247 }
248
249 gfx::NativeView TouchEditableImplAura::GetNativeView() const {
250   return rwhva_ ? rwhva_->GetNativeView()->GetToplevelWindow() : NULL;
251 }
252
253 void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) {
254   if (!rwhva_)
255     return;
256   aura::Window* window = rwhva_->GetNativeView();
257   aura::client::ScreenPositionClient* screen_position_client =
258       aura::client::GetScreenPositionClient(window->GetRootWindow());
259   if (screen_position_client)
260     screen_position_client->ConvertPointToScreen(window, point);
261 }
262
263 void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) {
264   if (!rwhva_)
265     return;
266   aura::Window* window = rwhva_->GetNativeView();
267   aura::client::ScreenPositionClient* screen_position_client =
268       aura::client::GetScreenPositionClient(window->GetRootWindow());
269   if (screen_position_client)
270     screen_position_client->ConvertPointFromScreen(window, point);
271 }
272
273 bool TouchEditableImplAura::DrawsHandles() {
274   return false;
275 }
276
277 void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) {
278   if (!rwhva_)
279     return;
280   gfx::Point point = anchor;
281   ConvertPointFromScreen(&point);
282   RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
283   host->Send(new ViewMsg_ShowContextMenu(
284       host->GetRoutingID(), ui::MENU_SOURCE_TOUCH_EDIT_MENU, point));
285   EndTouchEditing(false);
286 }
287
288 bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const {
289   NOTREACHED();
290   return false;
291 }
292
293 bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const {
294   if (!rwhva_)
295     return false;
296   bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
297   bool readable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD;
298   gfx::Range selection_range;
299   rwhva_->GetSelectionRange(&selection_range);
300   bool has_selection = !selection_range.is_empty();
301   switch (command_id) {
302     case IDS_APP_CUT:
303       return editable && readable && has_selection;
304     case IDS_APP_COPY:
305       return readable && has_selection;
306     case IDS_APP_PASTE: {
307       base::string16 result;
308       ui::Clipboard::GetForCurrentThread()->ReadText(
309           ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
310       return editable && !result.empty();
311     }
312     case IDS_APP_DELETE:
313       return editable && has_selection;
314     case IDS_APP_SELECT_ALL:
315       return true;
316     default:
317       return false;
318   }
319 }
320
321 bool TouchEditableImplAura::GetAcceleratorForCommandId(
322     int command_id,
323     ui::Accelerator* accelerator) {
324   return false;
325 }
326
327 void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
328   RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
329   RenderViewHost* rvh = RenderViewHost::From(host);
330   WebContents* wc = WebContents::FromRenderViewHost(rvh);
331
332   switch (command_id) {
333     case IDS_APP_CUT:
334       wc->Cut();
335       break;
336     case IDS_APP_COPY:
337       wc->Copy();
338       break;
339     case IDS_APP_PASTE:
340       wc->Paste();
341       break;
342     case IDS_APP_DELETE:
343       wc->Delete();
344       break;
345     case IDS_APP_SELECT_ALL:
346       wc->SelectAll();
347       break;
348     default:
349       NOTREACHED();
350       break;
351   }
352   EndTouchEditing(false);
353 }
354
355 void TouchEditableImplAura::DestroyTouchSelection() {
356   EndTouchEditing(false);
357 }
358
359 ////////////////////////////////////////////////////////////////////////////////
360 // TouchEditableImplAura, private:
361
362 TouchEditableImplAura::TouchEditableImplAura()
363     : text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
364       rwhva_(NULL),
365       selection_gesture_in_process_(false),
366       handles_hidden_due_to_scroll_(false),
367       scroll_in_progress_(false),
368       overscroll_in_progress_(false),
369       textfield_was_focused_on_tap_(false) {
370 }
371
372 void TouchEditableImplAura::Cleanup() {
373   if (rwhva_) {
374     rwhva_->set_touch_editing_client(NULL);
375     rwhva_ = NULL;
376   }
377   text_input_type_ = ui::TEXT_INPUT_TYPE_NONE;
378   EndTouchEditing(true);
379   selection_gesture_in_process_ = false;
380   handles_hidden_due_to_scroll_ = false;
381   scroll_in_progress_ = false;
382   overscroll_in_progress_ = false;
383 }
384
385 }  // namespace content