Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / desktop_window_tree_host_x11_unittest.cc
blob17eb63a8e798b8af8e91c79b991192c0a37ebdbd
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.
5 #include <vector>
7 #include <X11/extensions/shape.h>
8 #include <X11/Xlib.h>
10 // Get rid of X11 macros which conflict with gtest.
11 // It is necessary to include this header before the rest so that Bool can be
12 // undefined.
13 #include "ui/events/test/events_test_utils_x11.h"
14 #undef Bool
15 #undef None
17 #include "base/command_line.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/run_loop.h"
20 #include "ui/aura/window.h"
21 #include "ui/aura/window_tree_host.h"
22 #include "ui/base/hit_test.h"
23 #include "ui/base/x/x11_util.h"
24 #include "ui/events/devices/x11/touch_factory_x11.h"
25 #include "ui/events/platform/x11/x11_event_source.h"
26 #include "ui/gfx/geometry/point.h"
27 #include "ui/gfx/geometry/rect.h"
28 #include "ui/gfx/path.h"
29 #include "ui/gfx/switches.h"
30 #include "ui/gfx/x/x11_atom_cache.h"
31 #include "ui/views/test/views_test_base.h"
32 #include "ui/views/test/x11_property_change_waiter.h"
33 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
34 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
35 #include "ui/views/widget/widget_delegate.h"
36 #include "ui/views/window/non_client_view.h"
38 namespace views {
40 namespace {
42 const int kPointerDeviceId = 1;
44 // Blocks till the window state hint, |hint|, is set or unset.
45 class WMStateWaiter : public X11PropertyChangeWaiter {
46 public:
47 WMStateWaiter(XID window,
48 const char* hint,
49 bool wait_till_set)
50 : X11PropertyChangeWaiter(window, "_NET_WM_STATE"),
51 hint_(hint),
52 wait_till_set_(wait_till_set) {
54 const char* kAtomsToCache[] = {
55 hint,
56 NULL
58 atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache));
61 ~WMStateWaiter() override {}
63 private:
64 // X11PropertyChangeWaiter:
65 bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override {
66 std::vector<Atom> hints;
67 if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) {
68 std::vector<Atom>::iterator it = std::find(
69 hints.begin(),
70 hints.end(),
71 atom_cache_->GetAtom(hint_));
72 bool hint_set = (it != hints.end());
73 return hint_set != wait_till_set_;
75 return true;
78 scoped_ptr<ui::X11AtomCache> atom_cache_;
80 // The name of the hint to wait to get set or unset.
81 const char* hint_;
83 // Whether we are waiting for |hint| to be set or unset.
84 bool wait_till_set_;
86 DISALLOW_COPY_AND_ASSIGN(WMStateWaiter);
89 // A NonClientFrameView with a window mask with the bottom right corner cut out.
90 class ShapedNonClientFrameView : public NonClientFrameView {
91 public:
92 explicit ShapedNonClientFrameView() {
95 ~ShapedNonClientFrameView() override {}
97 // NonClientFrameView:
98 gfx::Rect GetBoundsForClientView() const override { return bounds(); }
99 gfx::Rect GetWindowBoundsForClientBounds(
100 const gfx::Rect& client_bounds) const override {
101 return client_bounds;
103 int NonClientHitTest(const gfx::Point& point) override { return HTNOWHERE; }
104 void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override {
105 int right = size.width();
106 int bottom = size.height();
108 window_mask->moveTo(0, 0);
109 window_mask->lineTo(0, bottom);
110 window_mask->lineTo(right, bottom);
111 window_mask->lineTo(right, 10);
112 window_mask->lineTo(right - 10, 10);
113 window_mask->lineTo(right - 10, 0);
114 window_mask->close();
116 void ResetWindowControls() override {}
117 void UpdateWindowIcon() override {}
118 void UpdateWindowTitle() override {}
119 void SizeConstraintsChanged() override {}
121 private:
122 DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView);
125 class ShapedWidgetDelegate : public WidgetDelegateView {
126 public:
127 ShapedWidgetDelegate() {
130 ~ShapedWidgetDelegate() override {}
132 // WidgetDelegateView:
133 NonClientFrameView* CreateNonClientFrameView(Widget* widget) override {
134 return new ShapedNonClientFrameView;
137 private:
138 DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate);
141 // Creates a widget of size 100x100.
142 scoped_ptr<Widget> CreateWidget(WidgetDelegate* delegate) {
143 scoped_ptr<Widget> widget(new Widget);
144 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
145 params.delegate = delegate;
146 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
147 params.remove_standard_frame = true;
148 params.native_widget = new DesktopNativeWidgetAura(widget.get());
149 params.bounds = gfx::Rect(100, 100, 100, 100);
150 widget->Init(params);
151 return widget.Pass();
154 // Returns the list of rectangles which describe |xid|'s bounding region via the
155 // X shape extension.
156 std::vector<gfx::Rect> GetShapeRects(XID xid) {
157 int dummy;
158 int shape_rects_size;
159 gfx::XScopedPtr<XRectangle[]> shape_rects(XShapeGetRectangles(
160 gfx::GetXDisplay(), xid, ShapeBounding, &shape_rects_size, &dummy));
162 std::vector<gfx::Rect> shape_vector;
163 for (int i = 0; i < shape_rects_size; ++i) {
164 const XRectangle& rect = shape_rects[i];
165 shape_vector.push_back(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
167 return shape_vector;
170 // Returns true if one of |rects| contains point (x,y).
171 bool ShapeRectContainsPoint(const std::vector<gfx::Rect>& shape_rects,
172 int x,
173 int y) {
174 gfx::Point point(x, y);
175 for (size_t i = 0; i < shape_rects.size(); ++i) {
176 if (shape_rects[i].Contains(point))
177 return true;
179 return false;
182 // Flush the message loop.
183 void RunAllPendingInMessageLoop() {
184 base::RunLoop run_loop;
185 run_loop.RunUntilIdle();
188 } // namespace
190 class DesktopWindowTreeHostX11Test : public ViewsTestBase {
191 public:
192 DesktopWindowTreeHostX11Test() {
194 ~DesktopWindowTreeHostX11Test() override {}
196 void SetUp() override {
197 ViewsTestBase::SetUp();
199 // Make X11 synchronous for our display connection. This does not force the
200 // window manager to behave synchronously.
201 XSynchronize(gfx::GetXDisplay(), True);
204 void TearDown() override {
205 XSynchronize(gfx::GetXDisplay(), False);
206 ViewsTestBase::TearDown();
209 private:
210 DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
213 // Tests that the shape is properly set on the x window.
214 TEST_F(DesktopWindowTreeHostX11Test, Shape) {
215 if (!ui::IsShapeExtensionAvailable())
216 return;
218 // 1) Test setting the window shape via the NonClientFrameView. This technique
219 // is used to get rounded corners on Chrome windows when not using the native
220 // window frame.
221 scoped_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate());
222 widget1->Show();
223 ui::X11EventSource::GetInstance()->DispatchXEvents();
225 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
226 std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1);
227 ASSERT_FALSE(shape_rects.empty());
229 // The widget was supposed to be 100x100, but the WM might have ignored this
230 // suggestion.
231 int widget_width = widget1->GetWindowBoundsInScreen().width();
232 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 15, 5));
233 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 5));
234 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 15));
235 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width + 5, 15));
237 // Changing widget's size should update the shape.
238 widget1->SetBounds(gfx::Rect(100, 100, 200, 200));
239 ui::X11EventSource::GetInstance()->DispatchXEvents();
241 if (widget1->GetWindowBoundsInScreen().width() == 200) {
242 shape_rects = GetShapeRects(xid1);
243 ASSERT_FALSE(shape_rects.empty());
244 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
245 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5));
246 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5));
247 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5));
248 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15));
249 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15));
252 if (ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"))) {
253 // The shape should be changed to a rectangle which fills the entire screen
254 // when |widget1| is maximized.
256 WMStateWaiter waiter(xid1, "_NET_WM_STATE_MAXIMIZED_VERT", true);
257 widget1->Maximize();
258 waiter.Wait();
261 // Ensure that the task which is posted when a window is resized is run.
262 RunAllPendingInMessageLoop();
264 // xvfb does not support Xrandr so we cannot check the maximized window's
265 // bounds.
266 gfx::Rect maximized_bounds;
267 ui::GetOuterWindowBounds(xid1, &maximized_bounds);
269 shape_rects = GetShapeRects(xid1);
270 ASSERT_FALSE(shape_rects.empty());
271 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
272 maximized_bounds.width() - 1,
273 5));
274 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
275 maximized_bounds.width() - 1,
276 15));
279 // 2) Test setting the window shape via Widget::SetShape().
280 gfx::Path shape2;
281 shape2.moveTo(10, 0);
282 shape2.lineTo(10, 10);
283 shape2.lineTo(0, 10);
284 shape2.lineTo(0, 100);
285 shape2.lineTo(100, 100);
286 shape2.lineTo(100, 0);
287 shape2.close();
289 scoped_ptr<Widget> widget2(CreateWidget(NULL));
290 widget2->Show();
291 widget2->SetShape(shape2.CreateNativeRegion());
292 ui::X11EventSource::GetInstance()->DispatchXEvents();
294 XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
295 shape_rects = GetShapeRects(xid2);
296 ASSERT_FALSE(shape_rects.empty());
297 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
298 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
299 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
300 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
302 // Changing the widget's size should not affect the shape.
303 widget2->SetBounds(gfx::Rect(100, 100, 200, 200));
304 shape_rects = GetShapeRects(xid2);
305 ASSERT_FALSE(shape_rects.empty());
306 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
307 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
308 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
309 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
311 // Setting the shape to NULL resets the shape back to the entire
312 // window bounds.
313 widget2->SetShape(NULL);
314 shape_rects = GetShapeRects(xid2);
315 ASSERT_FALSE(shape_rects.empty());
316 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 5, 5));
317 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
318 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
319 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 105, 15));
320 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 500, 500));
323 // Test that the widget ignores changes in fullscreen state initiated by the
324 // window manager (e.g. via a window manager accelerator key).
325 TEST_F(DesktopWindowTreeHostX11Test, WindowManagerTogglesFullscreen) {
326 if (!ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_FULLSCREEN")))
327 return;
329 scoped_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate());
330 XID xid = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
331 widget->Show();
332 ui::X11EventSource::GetInstance()->DispatchXEvents();
334 gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
336 WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", true);
337 widget->SetFullscreen(true);
338 waiter.Wait();
340 EXPECT_TRUE(widget->IsFullscreen());
342 // Emulate the window manager exiting fullscreen via a window manager
343 // accelerator key. It should not affect the widget's fullscreen state.
345 const char* kAtomsToCache[] = {
346 "_NET_WM_STATE",
347 "_NET_WM_STATE_FULLSCREEN",
348 NULL
350 Display* display = gfx::GetXDisplay();
351 ui::X11AtomCache atom_cache(display, kAtomsToCache);
353 XEvent xclient;
354 memset(&xclient, 0, sizeof(xclient));
355 xclient.type = ClientMessage;
356 xclient.xclient.window = xid;
357 xclient.xclient.message_type = atom_cache.GetAtom("_NET_WM_STATE");
358 xclient.xclient.format = 32;
359 xclient.xclient.data.l[0] = 0;
360 xclient.xclient.data.l[1] = atom_cache.GetAtom("_NET_WM_STATE_FULLSCREEN");
361 xclient.xclient.data.l[2] = 0;
362 xclient.xclient.data.l[3] = 1;
363 xclient.xclient.data.l[4] = 0;
364 XSendEvent(display, DefaultRootWindow(display), False,
365 SubstructureRedirectMask | SubstructureNotifyMask,
366 &xclient);
368 WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", false);
369 waiter.Wait();
371 EXPECT_TRUE(widget->IsFullscreen());
373 // Calling Widget::SetFullscreen(false) should clear the widget's fullscreen
374 // state and clean things up.
375 widget->SetFullscreen(false);
376 EXPECT_FALSE(widget->IsFullscreen());
377 EXPECT_EQ(initial_bounds.ToString(),
378 widget->GetWindowBoundsInScreen().ToString());
381 // Tests that the minimization information is propagated to the content window.
382 TEST_F(DesktopWindowTreeHostX11Test, ToggleMinimizePropogateToContentWindow) {
383 Widget widget;
384 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
385 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
386 params.native_widget = new DesktopNativeWidgetAura(&widget);
387 widget.Init(params);
388 widget.Show();
389 ui::X11EventSource::GetInstance()->DispatchXEvents();
391 XID xid = widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget();
392 Display* display = gfx::GetXDisplay();
394 // Minimize by sending _NET_WM_STATE_HIDDEN
396 const char* kAtomsToCache[] = {
397 "_NET_WM_STATE",
398 "_NET_WM_STATE_HIDDEN",
399 NULL
402 ui::X11AtomCache atom_cache(display, kAtomsToCache);
404 std::vector< ::Atom> atom_list;
405 atom_list.push_back(atom_cache.GetAtom("_NET_WM_STATE_HIDDEN"));
406 ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
408 XEvent xevent;
409 memset(&xevent, 0, sizeof(xevent));
410 xevent.type = PropertyNotify;
411 xevent.xproperty.type = PropertyNotify;
412 xevent.xproperty.send_event = 1;
413 xevent.xproperty.display = display;
414 xevent.xproperty.window = xid;
415 xevent.xproperty.atom = atom_cache.GetAtom("_NET_WM_STATE");
416 xevent.xproperty.state = 0;
417 XSendEvent(display, DefaultRootWindow(display), False,
418 SubstructureRedirectMask | SubstructureNotifyMask,
419 &xevent);
421 WMStateWaiter waiter(xid, "_NET_WM_STATE_HIDDEN", true);
422 waiter.Wait();
424 EXPECT_FALSE(widget.GetNativeWindow()->IsVisible());
426 // Show from minimized by sending _NET_WM_STATE_FOCUSED
428 const char* kAtomsToCache[] = {
429 "_NET_WM_STATE",
430 "_NET_WM_STATE_FOCUSED",
431 NULL
434 ui::X11AtomCache atom_cache(display, kAtomsToCache);
436 std::vector< ::Atom> atom_list;
437 atom_list.push_back(atom_cache.GetAtom("_NET_WM_STATE_FOCUSED"));
438 ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list);
440 XEvent xevent;
441 memset(&xevent, 0, sizeof(xevent));
442 xevent.type = PropertyNotify;
443 xevent.xproperty.type = PropertyNotify;
444 xevent.xproperty.send_event = 1;
445 xevent.xproperty.display = display;
446 xevent.xproperty.window = xid;
447 xevent.xproperty.atom = atom_cache.GetAtom("_NET_WM_STATE");
448 xevent.xproperty.state = 0;
449 XSendEvent(display, DefaultRootWindow(display), False,
450 SubstructureRedirectMask | SubstructureNotifyMask,
451 &xevent);
453 WMStateWaiter waiter(xid, "_NET_WM_STATE_FOCUSED", true);
454 waiter.Wait();
456 EXPECT_TRUE(widget.GetNativeWindow()->IsVisible());
459 class MouseEventRecorder : public ui::EventHandler {
460 public:
461 MouseEventRecorder() {}
462 ~MouseEventRecorder() override {}
464 void Reset() { mouse_events_.clear(); }
466 const std::vector<ui::MouseEvent>& mouse_events() const {
467 return mouse_events_;
470 private:
471 // ui::EventHandler:
472 void OnMouseEvent(ui::MouseEvent* mouse) override {
473 mouse_events_.push_back(*mouse);
476 std::vector<ui::MouseEvent> mouse_events_;
478 DISALLOW_COPY_AND_ASSIGN(MouseEventRecorder);
481 // A custom event-source that can be used to directly dispatch synthetic X11
482 // events.
483 class CustomX11EventSource : public ui::X11EventSource {
484 public:
485 CustomX11EventSource() : X11EventSource(gfx::GetXDisplay()) {}
486 ~CustomX11EventSource() override {}
488 void DispatchSingleEvent(XEvent* xevent) {
489 PlatformEventSource::DispatchEvent(xevent);
492 private:
493 DISALLOW_COPY_AND_ASSIGN(CustomX11EventSource);
496 class DesktopWindowTreeHostX11HighDPITest
497 : public DesktopWindowTreeHostX11Test {
498 public:
499 DesktopWindowTreeHostX11HighDPITest() {}
500 ~DesktopWindowTreeHostX11HighDPITest() override {}
502 void DispatchSingleEventToWidget(XEvent* event, Widget* widget) {
503 DCHECK_EQ(GenericEvent, event->type);
504 XIDeviceEvent* device_event =
505 static_cast<XIDeviceEvent*>(event->xcookie.data);
506 device_event->event =
507 widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
508 event_source_.DispatchSingleEvent(event);
511 void PretendCapture(views::Widget* capture_widget) {
512 DesktopWindowTreeHostX11* capture_host = nullptr;
513 if (capture_widget) {
514 capture_host = static_cast<DesktopWindowTreeHostX11*>(
515 capture_widget->GetNativeWindow()->GetHost());
517 DesktopWindowTreeHostX11::g_current_capture = capture_host;
518 if (capture_widget)
519 capture_widget->GetNativeWindow()->SetCapture();
522 private:
523 void SetUp() override {
524 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
525 command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor, "2");
526 std::vector<int> pointer_devices;
527 pointer_devices.push_back(kPointerDeviceId);
528 ui::TouchFactory::GetInstance()->SetPointerDeviceForTest(pointer_devices);
530 DesktopWindowTreeHostX11Test::SetUp();
533 CustomX11EventSource event_source_;
534 DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11HighDPITest);
537 TEST_F(DesktopWindowTreeHostX11HighDPITest, LocatedEventDispatchWithCapture) {
538 Widget first;
539 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
540 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
541 params.native_widget = new DesktopNativeWidgetAura(&first);
542 params.bounds = gfx::Rect(0, 0, 50, 50);
543 first.Init(params);
544 first.Show();
546 Widget second;
547 params = CreateParams(Widget::InitParams::TYPE_WINDOW);
548 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
549 params.native_widget = new DesktopNativeWidgetAura(&second);
550 params.bounds = gfx::Rect(50, 50, 50, 50);
551 second.Init(params);
552 second.Show();
554 ui::X11EventSource::GetInstance()->DispatchXEvents();
556 MouseEventRecorder first_recorder, second_recorder;
557 first.GetNativeWindow()->AddPreTargetHandler(&first_recorder);
558 second.GetNativeWindow()->AddPreTargetHandler(&second_recorder);
560 // Dispatch an event on |first|. Verify it gets the event.
561 ui::ScopedXI2Event event;
562 event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSEWHEEL,
563 gfx::Point(50, 50), ui::EF_NONE);
564 DispatchSingleEventToWidget(event, &first);
565 ASSERT_EQ(1u, first_recorder.mouse_events().size());
566 EXPECT_EQ(ui::ET_MOUSEWHEEL, first_recorder.mouse_events()[0].type());
567 EXPECT_EQ(gfx::Point(25, 25).ToString(),
568 first_recorder.mouse_events()[0].location().ToString());
569 ASSERT_EQ(0u, second_recorder.mouse_events().size());
571 first_recorder.Reset();
572 second_recorder.Reset();
574 // Set a capture on |second|, and dispatch the same event to |first|. This
575 // event should reach |second| instead.
576 PretendCapture(&second);
577 event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSEWHEEL,
578 gfx::Point(50, 50), ui::EF_NONE);
579 DispatchSingleEventToWidget(event, &first);
581 ASSERT_EQ(0u, first_recorder.mouse_events().size());
582 ASSERT_EQ(1u, second_recorder.mouse_events().size());
583 EXPECT_EQ(ui::ET_MOUSEWHEEL, second_recorder.mouse_events()[0].type());
584 EXPECT_EQ(gfx::Point(-25, -25).ToString(),
585 second_recorder.mouse_events()[0].location().ToString());
587 PretendCapture(nullptr);
588 first.GetNativeWindow()->RemovePreTargetHandler(&first_recorder);
589 second.GetNativeWindow()->RemovePreTargetHandler(&second_recorder);
592 } // namespace views