1// Copyright 2014 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 "base/bind.h"
6#include "base/logging.h"
7#include "ui/aura/client/screen_position_client.h"
8#include "ui/aura/env.h"
9#include "ui/aura/test/aura_test_utils.h"
10#include "ui/aura/test/ui_controls_factory_aura.h"
11#include "ui/aura/window_tree_host.h"
12#include "ui/base/test/ui_controls_aura.h"
13
14namespace aura {
15namespace test {
16namespace {
17
18class UIControlsOzone : public ui_controls::UIControlsAura {
19 public:
20  UIControlsOzone(WindowTreeHost* host) : host_(host) {}
21
22  virtual bool SendKeyPress(gfx::NativeWindow window,
23                            ui::KeyboardCode key,
24                            bool control,
25                            bool shift,
26                            bool alt,
27                            bool command) OVERRIDE {
28    return SendKeyPressNotifyWhenDone(
29        window, key, control, shift, alt, command, base::Closure());
30  }
31  virtual bool SendKeyPressNotifyWhenDone(
32      gfx::NativeWindow window,
33      ui::KeyboardCode key,
34      bool control,
35      bool shift,
36      bool alt,
37      bool command,
38      const base::Closure& closure) OVERRIDE {
39    int flags = button_down_mask_;
40
41    if (control) {
42      flags |= ui::EF_CONTROL_DOWN;
43      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, flags);
44    }
45
46    if (shift) {
47      flags |= ui::EF_SHIFT_DOWN;
48      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, flags);
49    }
50
51    if (alt) {
52      flags |= ui::EF_ALT_DOWN;
53      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_MENU, flags);
54    }
55
56    if (command) {
57      flags |= ui::EF_COMMAND_DOWN;
58      PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LWIN, flags);
59    }
60
61    PostKeyEvent(ui::ET_KEY_PRESSED, key, flags);
62    PostKeyEvent(ui::ET_KEY_RELEASED, key, flags);
63
64    if (alt) {
65      flags &= ~ui::EF_ALT_DOWN;
66      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_MENU, flags);
67    }
68
69    if (shift) {
70      flags &= ~ui::EF_SHIFT_DOWN;
71      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, flags);
72    }
73
74    if (control) {
75      flags &= ~ui::EF_CONTROL_DOWN;
76      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, flags);
77    }
78
79    if (command) {
80      flags &= ~ui::EF_COMMAND_DOWN;
81      PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_LWIN, flags);
82    }
83
84    RunClosureAfterAllPendingUIEvents(closure);
85    return true;
86  }
87
88  virtual bool SendMouseMove(long screen_x, long screen_y) OVERRIDE {
89    return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::Closure());
90  }
91  virtual bool SendMouseMoveNotifyWhenDone(
92      long screen_x,
93      long screen_y,
94      const base::Closure& closure) OVERRIDE {
95    gfx::Point root_location(screen_x, screen_y);
96    aura::client::ScreenPositionClient* screen_position_client =
97        aura::client::GetScreenPositionClient(host_->window());
98    if (screen_position_client) {
99      screen_position_client->ConvertPointFromScreen(host_->window(),
100                                                     &root_location);
101    }
102    gfx::Point root_current_location =
103        QueryLatestMousePositionRequestInHost(host_);
104    host_->ConvertPointFromHost(&root_current_location);
105
106    if (button_down_mask_)
107      PostMouseEvent(ui::ET_MOUSE_DRAGGED, root_location, 0, 0);
108    else
109      PostMouseEvent(ui::ET_MOUSE_MOVED, root_location, 0, 0);
110
111    RunClosureAfterAllPendingUIEvents(closure);
112    return true;
113  }
114  virtual bool SendMouseEvents(ui_controls::MouseButton type,
115                               int state) OVERRIDE {
116    return SendMouseEventsNotifyWhenDone(type, state, base::Closure());
117  }
118  virtual bool SendMouseEventsNotifyWhenDone(
119      ui_controls::MouseButton type,
120      int state,
121      const base::Closure& closure) OVERRIDE {
122    gfx::Point loc = aura::Env::GetInstance()->last_mouse_location();
123    aura::client::ScreenPositionClient* screen_position_client =
124        aura::client::GetScreenPositionClient(host_->window());
125    if (screen_position_client) {
126      screen_position_client->ConvertPointFromScreen(host_->window(), &loc);
127    }
128    int flag = 0;
129
130    switch (type) {
131      case ui_controls::LEFT:
132        flag = ui::EF_LEFT_MOUSE_BUTTON;
133        break;
134      case ui_controls::MIDDLE:
135        flag = ui::EF_MIDDLE_MOUSE_BUTTON;
136        break;
137      case ui_controls::RIGHT:
138        flag = ui::EF_RIGHT_MOUSE_BUTTON;
139        break;
140      default:
141        NOTREACHED();
142        break;
143    }
144
145    if (state & ui_controls::DOWN) {
146      button_down_mask_ |= flag;
147      PostMouseEvent(ui::ET_MOUSE_PRESSED, loc, button_down_mask_ | flag, flag);
148    }
149    if (state & ui_controls::UP) {
150      button_down_mask_ &= ~flag;
151      PostMouseEvent(
152          ui::ET_MOUSE_RELEASED, loc, button_down_mask_ | flag, flag);
153    }
154
155    RunClosureAfterAllPendingUIEvents(closure);
156    return true;
157  }
158  virtual bool SendMouseClick(ui_controls::MouseButton type) OVERRIDE {
159    return SendMouseEvents(type, ui_controls::UP | ui_controls::DOWN);
160  }
161  virtual void RunClosureAfterAllPendingUIEvents(
162      const base::Closure& closure) OVERRIDE {
163    if (!closure.is_null())
164      base::MessageLoop::current()->PostTask(FROM_HERE, closure);
165  }
166
167 private:
168  void PostKeyEvent(ui::EventType type, ui::KeyboardCode key_code, int flags) {
169    base::MessageLoop::current()->PostTask(
170        FROM_HERE,
171        base::Bind(&UIControlsOzone::PostKeyEventTask,
172                   base::Unretained(this),
173                   type,
174                   key_code,
175                   flags));
176  }
177
178  void PostKeyEventTask(ui::EventType type,
179                        ui::KeyboardCode key_code,
180                        int flags) {
181    // Do not rewrite injected events. See crbug.com/136465.
182    flags |= ui::EF_FINAL;
183
184    ui::KeyEvent key_event(type, key_code, flags);
185    host_->PostNativeEvent(&key_event);
186  }
187
188  void PostMouseEvent(ui::EventType type,
189                      const gfx::PointF& location,
190                      int flags,
191                      int changed_button_flags) {
192    base::MessageLoop::current()->PostTask(
193        FROM_HERE,
194        base::Bind(&UIControlsOzone::PostMouseEventTask,
195                   base::Unretained(this),
196                   type,
197                   location,
198                   flags,
199                   changed_button_flags));
200  }
201
202  void PostMouseEventTask(ui::EventType type,
203                          const gfx::PointF& location,
204                          int flags,
205                          int changed_button_flags) {
206    ui::MouseEvent mouse_event(
207        type, location, location, flags, changed_button_flags);
208
209    // This hack is necessary to set the repeat count for clicks.
210    ui::MouseEvent mouse_event2(&mouse_event);
211
212    host_->PostNativeEvent(&mouse_event2);
213  }
214
215  WindowTreeHost* host_;
216
217  // Mask of the mouse buttons currently down.
218  unsigned button_down_mask_ = 0;
219
220  DISALLOW_COPY_AND_ASSIGN(UIControlsOzone);
221};
222
223}  // namespace
224
225ui_controls::UIControlsAura* CreateUIControlsAura(WindowTreeHost* host) {
226  return new UIControlsOzone(host);
227}
228
229}  // namespace test
230}  // namespace aura
231