Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / web_contents / touch_editable_impl_aura.cc
blob622b49045ec28535f7c8368ca763e6ba4bff4ba7
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.
5 #include "content/browser/web_contents/touch_editable_impl_aura.h"
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 "ui/aura/client/screen_position_client.h"
14 #include "ui/aura/window.h"
15 #include "ui/aura/window_tree_host.h"
16 #include "ui/base/clipboard/clipboard.h"
17 #include "ui/base/touch/selection_bound.h"
18 #include "ui/base/ui_base_switches_util.h"
19 #include "ui/gfx/range/range.h"
20 #include "ui/strings/grit/ui_strings.h"
21 #include "ui/wm/public/activation_client.h"
23 namespace content {
25 ////////////////////////////////////////////////////////////////////////////////
26 // TouchEditableImplAura, public:
28 TouchEditableImplAura::~TouchEditableImplAura() {
29 Cleanup();
32 // static
33 TouchEditableImplAura* TouchEditableImplAura::Create() {
34 if (switches::IsTouchEditingEnabled())
35 return new TouchEditableImplAura();
36 return NULL;
39 void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura* view) {
40 if (rwhva_ == view)
41 return;
43 Cleanup();
44 if (!view)
45 return;
47 rwhva_ = view;
48 rwhva_->set_touch_editing_client(this);
51 void TouchEditableImplAura::UpdateEditingController() {
52 if (!rwhva_ || !rwhva_->HasFocus())
53 return;
55 if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE ||
56 selection_anchor_ != selection_focus_) {
57 if (touch_selection_controller_)
58 touch_selection_controller_->SelectionChanged();
59 } else {
60 EndTouchEditing(false);
64 void TouchEditableImplAura::OverscrollStarted() {
65 overscroll_in_progress_ = true;
68 void TouchEditableImplAura::OverscrollCompleted() {
69 overscroll_in_progress_ = false;
70 StartTouchEditingIfNecessary();
73 ////////////////////////////////////////////////////////////////////////////////
74 // TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient
75 // implementation:
77 void TouchEditableImplAura::StartTouchEditing() {
78 if (!rwhva_ || !rwhva_->HasFocus())
79 return;
81 if (!touch_selection_controller_) {
82 touch_selection_controller_.reset(
83 ui::TouchEditingControllerDeprecated::Create(this));
85 if (touch_selection_controller_)
86 touch_selection_controller_->SelectionChanged();
89 void TouchEditableImplAura::EndTouchEditing(bool quick) {
90 if (touch_selection_controller_) {
91 if (touch_selection_controller_->IsHandleDragInProgress()) {
92 touch_selection_controller_->SelectionChanged();
93 } else {
94 selection_gesture_in_process_ = false;
95 touch_selection_controller_->HideHandles(quick);
96 touch_selection_controller_.reset();
101 void TouchEditableImplAura::OnSelectionOrCursorChanged(
102 const ui::SelectionBound& anchor,
103 const ui::SelectionBound& focus) {
104 selection_anchor_ = anchor;
105 selection_focus_ = focus;
107 // If touch editing handles were not visible, we bring them up only if the
108 // current event is a gesture event, no scroll/fling/overscoll is in progress,
109 // and there is non-zero selection on the page
110 if (selection_gesture_in_process_ && !scroll_in_progress_ &&
111 !overscroll_in_progress_ && selection_anchor_ != selection_focus_) {
112 StartTouchEditing();
113 selection_gesture_in_process_ = false;
116 UpdateEditingController();
119 void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) {
120 text_input_type_ = type;
123 bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
124 DCHECK(rwhva_);
125 if (!event->IsGestureEvent()) {
126 // Ignore all non-gesture events. Non-gesture events that can deactivate
127 // touch editing are handled in TouchSelectionControllerImpl.
128 return false;
131 const ui::GestureEvent* gesture_event =
132 static_cast<const ui::GestureEvent*>(event);
133 switch (event->type()) {
134 case ui::ET_GESTURE_TAP:
135 // When the user taps, we want to show touch editing handles if user
136 // tapped on selected text.
137 if (gesture_event->details().tap_count() == 1 &&
138 selection_anchor_ != selection_focus_) {
139 gfx::Rect selection_rect =
140 ui::RectBetweenSelectionBounds(selection_anchor_, selection_focus_);
141 // When tap is on selection, show handles and mark event as handled only
142 // if handles are not present or text is not editable. Otherwise, do not
143 // set event as handles so that event is forwarded to the renderer to
144 // update selection/cursor.
145 if (selection_rect.Contains(gesture_event->location()) &&
146 (text_input_type_ == ui::TEXT_INPUT_TYPE_NONE ||
147 !touch_selection_controller_)) {
148 StartTouchEditing();
149 return true;
152 // For single taps, not inside selected region, we want to show handles
153 // only when the tap is on an already focused textfield.
154 textfield_was_focused_on_tap_ =
155 gesture_event->details().tap_count() == 1 &&
156 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE;
157 break;
158 case ui::ET_GESTURE_LONG_PRESS:
159 selection_gesture_in_process_ = true;
160 break;
161 case ui::ET_GESTURE_SCROLL_BEGIN:
162 scroll_in_progress_ = true;;
163 // We need to hide selection handles during scroll (including fling and
164 // overscroll), but they should be re-activated after scrolling if:
165 // - an existing scroll decided that handles should be shown after
166 // scrolling; or
167 // - the gesture in progress is going to end in selection; or
168 // - selection handles are currently active.
169 handles_hidden_due_to_scroll_ = handles_hidden_due_to_scroll_ ||
170 selection_gesture_in_process_ ||
171 touch_selection_controller_ != NULL;
172 selection_gesture_in_process_ = false;
173 EndTouchEditing(true);
174 break;
175 case ui::ET_GESTURE_SCROLL_END:
176 scroll_in_progress_ = false;
177 StartTouchEditingIfNecessary();
178 break;
179 default:
180 break;
182 return false;
185 void TouchEditableImplAura::GestureEventAck(int gesture_event_type) {
186 DCHECK(rwhva_);
187 if (gesture_event_type == blink::WebInputEvent::GestureTap &&
188 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
189 textfield_was_focused_on_tap_) {
190 StartTouchEditing();
191 UpdateEditingController();
195 void TouchEditableImplAura::DidStopFlinging() {
196 scroll_in_progress_ = false;
197 StartTouchEditingIfNecessary();
200 void TouchEditableImplAura::OnViewDestroyed() {
201 Cleanup();
204 ////////////////////////////////////////////////////////////////////////////////
205 // TouchEditableImplAura, ui::TouchEditable implementation:
207 void TouchEditableImplAura::SelectRect(const gfx::Point& start,
208 const gfx::Point& end) {
209 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
210 RenderViewHost* rvh = RenderViewHost::From(host);
211 WebContentsImpl* wc =
212 static_cast<WebContentsImpl*>(WebContents::FromRenderViewHost(rvh));
213 wc->SelectRange(start, end);
216 void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) {
217 if (!rwhva_)
218 return;
220 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
221 rwhva_->GetRenderWidgetHost());
222 host->MoveCaret(point);
225 void TouchEditableImplAura::GetSelectionEndPoints(ui::SelectionBound* anchor,
226 ui::SelectionBound* focus) {
227 *anchor = selection_anchor_;
228 *focus = selection_focus_;
231 gfx::Rect TouchEditableImplAura::GetBounds() {
232 return rwhva_ ? gfx::Rect(rwhva_->GetNativeView()->bounds().size()) :
233 gfx::Rect();
236 gfx::NativeView TouchEditableImplAura::GetNativeView() const {
237 return rwhva_ ? rwhva_->GetNativeView()->GetToplevelWindow() : NULL;
240 void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) {
241 if (!rwhva_)
242 return;
243 aura::Window* window = rwhva_->GetNativeView();
244 aura::client::ScreenPositionClient* screen_position_client =
245 aura::client::GetScreenPositionClient(window->GetRootWindow());
246 if (screen_position_client)
247 screen_position_client->ConvertPointToScreen(window, point);
250 void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) {
251 if (!rwhva_)
252 return;
253 aura::Window* window = rwhva_->GetNativeView();
254 aura::client::ScreenPositionClient* screen_position_client =
255 aura::client::GetScreenPositionClient(window->GetRootWindow());
256 if (screen_position_client)
257 screen_position_client->ConvertPointFromScreen(window, point);
260 bool TouchEditableImplAura::DrawsHandles() {
261 return false;
264 void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) {
265 if (!rwhva_)
266 return;
267 gfx::Point point = anchor;
268 ConvertPointFromScreen(&point);
269 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
270 host->Send(new ViewMsg_ShowContextMenu(
271 host->GetRoutingID(), ui::MENU_SOURCE_TOUCH_EDIT_MENU, point));
272 EndTouchEditing(false);
275 bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const {
276 NOTREACHED();
277 return false;
280 bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const {
281 if (!rwhva_)
282 return false;
283 bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
284 bool readable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD;
285 gfx::Range selection_range;
286 rwhva_->GetSelectionRange(&selection_range);
287 bool has_selection = !selection_range.is_empty();
288 switch (command_id) {
289 case IDS_APP_CUT:
290 return editable && readable && has_selection;
291 case IDS_APP_COPY:
292 return readable && has_selection;
293 case IDS_APP_PASTE: {
294 base::string16 result;
295 ui::Clipboard::GetForCurrentThread()->ReadText(
296 ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
297 return editable && !result.empty();
299 case IDS_APP_DELETE:
300 return editable && has_selection;
301 case IDS_APP_SELECT_ALL:
302 return true;
303 default:
304 return false;
308 bool TouchEditableImplAura::GetAcceleratorForCommandId(
309 int command_id,
310 ui::Accelerator* accelerator) {
311 return false;
314 void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
315 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
316 RenderViewHost* rvh = RenderViewHost::From(host);
317 WebContents* wc = WebContents::FromRenderViewHost(rvh);
319 switch (command_id) {
320 case IDS_APP_CUT:
321 wc->Cut();
322 break;
323 case IDS_APP_COPY:
324 wc->Copy();
325 break;
326 case IDS_APP_PASTE:
327 wc->Paste();
328 break;
329 case IDS_APP_DELETE:
330 wc->Delete();
331 break;
332 case IDS_APP_SELECT_ALL:
333 wc->SelectAll();
334 break;
335 default:
336 NOTREACHED();
337 break;
339 EndTouchEditing(false);
342 void TouchEditableImplAura::DestroyTouchSelection() {
343 EndTouchEditing(false);
346 ////////////////////////////////////////////////////////////////////////////////
347 // TouchEditableImplAura, private:
349 TouchEditableImplAura::TouchEditableImplAura()
350 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
351 rwhva_(NULL),
352 selection_gesture_in_process_(false),
353 handles_hidden_due_to_scroll_(false),
354 scroll_in_progress_(false),
355 overscroll_in_progress_(false),
356 textfield_was_focused_on_tap_(false) {
359 void TouchEditableImplAura::StartTouchEditingIfNecessary() {
360 // If there is no scrolling left in progress, show selection handles if they
361 // were hidden due to scroll and there is a selection.
362 if (!scroll_in_progress_ && !overscroll_in_progress_ &&
363 handles_hidden_due_to_scroll_ &&
364 (selection_anchor_ != selection_focus_ ||
365 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) {
366 StartTouchEditing();
367 UpdateEditingController();
368 handles_hidden_due_to_scroll_ = false;
372 void TouchEditableImplAura::Cleanup() {
373 if (rwhva_) {
374 rwhva_->set_touch_editing_client(NULL);
375 rwhva_ = NULL;
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;
385 } // namespace content