15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/display/mouse_cursor_event_filter.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/shell.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/test/ash_test_base.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/test/cursor_manager_test_api.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ash/display/display_controller.h"
117dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "ash/display/display_layout_store.h"
127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "ash/display/display_manager.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/env.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/root_window.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/display.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ash {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace internal {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Display GetPrimaryDisplay() {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Shell::GetScreen()->GetDisplayNearestWindow(
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetAllRootWindows()[0]);
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Display GetSecondaryDisplay() {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Shell::GetScreen()->GetDisplayNearestWindow(
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetAllRootWindows()[1]);
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef test::AshTestBase MouseCursorEventFilterTest;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Verifies if the mouse pointer correctly moves to another display when there
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// are two displays.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(MouseCursorEventFilterTest, WarpMouse) {
397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDisplay("500x500,500x500");
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MouseCursorEventFilter* event_filter =
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DisplayLayout::RIGHT,
487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      Shell::GetInstance()->display_manager()->layout_store()->
497dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch          default_display_layout().position);
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[0],
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                            gfx::Point(11, 11));
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       gfx::Point(11, 11));
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the right edge of the primary root window. Pointer should warp.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[0],
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       gfx::Point(499, 11));
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(is_warped);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("501,11",  // by 2px.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the left edge of the secondary root window. Pointer should warp.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       gfx::Point(500, 11));
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(is_warped);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("498,11",  // by 2px.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the left edge of the primary root window.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[0],
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     gfx::Point(0, 11));
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the top edge of the primary root window.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[0],
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     gfx::Point(11, 0));
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the bottom edge of the primary root window.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[0],
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     gfx::Point(11, 499));
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the right edge of the secondary root window.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     gfx::Point(999, 11));
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the top edge of the secondary root window.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     gfx::Point(11, 0));
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the bottom edge of the secondary root window.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     gfx::Point(11, 499));
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Verifies if the mouse pointer correctly moves to another display even when
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// two displays are not the same size.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(MouseCursorEventFilterTest, WarpMouseDifferentSizeDisplays) {
1027d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
1037d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
1047d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDisplay("500x500,600x600");  // the second one is larger.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MouseCursorEventFilter* event_filter =
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DisplayLayout::RIGHT,
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      Shell::GetInstance()->display_controller()->
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          GetCurrentDisplayLayout().position);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(623, 123));
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the left edge of the secondary root window. Pointer should NOT warp
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because 1px left of (0, 500) is outside the primary root window.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                            gfx::Point(0, 500));
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("623,123",  // by 2px.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Touch the left edge of the secondary root window. Pointer should warp
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because 1px left of (0, 499) is inside the primary root window.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     gfx::Point(500, 499));
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(is_warped);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("498,499",  // by 2px.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Verifies if the mouse pointer correctly moves between displays with
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// different scale factors.
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(MouseCursorEventFilterTest, WarpMouseDifferentScaleDisplays) {
1377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
1387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
1397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UpdateDisplay("500x500,600x600*2");
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MouseCursorEventFilter* event_filter =
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_EQ(
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DisplayLayout::RIGHT,
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      Shell::GetInstance()->display_controller()->
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          GetCurrentDisplayLayout().position);
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(900, 123));
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This emulates the dragging back to the 2nd display, which has
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // higher scale factor, by having 2nd display's root as target
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // but have the edge of 1st display.
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_warped =
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      event_filter->WarpMouseCursorIfNecessary(root_windows[1],
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                               gfx::Point(498, 123));
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(is_warped);
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ("502,123",
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Touch the edge of 2nd display again and make sure it warps to
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // 1st dislay.
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[1],
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                       gfx::Point(500, 123));
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(is_warped);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ("496,123",
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Verifies if MouseCursorEventFilter::set_mouse_warp_mode() works as expected.
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(MouseCursorEventFilterTest, SetMouseWarpModeFlag) {
1737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
1747d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
1757d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDisplay("500x500,500x500");
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MouseCursorEventFilter* event_filter =
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(1, 1));
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->set_mouse_warp_mode(MouseCursorEventFilter::WARP_NONE);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[0],
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                          gfx::Point(499, 11));
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(is_warped);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("1,1",
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->set_mouse_warp_mode(MouseCursorEventFilter::WARP_ALWAYS);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_warped = event_filter->WarpMouseCursorIfNecessary(root_windows[0],
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       gfx::Point(499, 11));
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(is_warped);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("501,11",
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            aura::Env::GetInstance()->last_mouse_location().ToString());
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Verifies if MouseCursorEventFilter's bounds calculation works correctly.
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(MouseCursorEventFilterTest, IndicatorBoundsTestOnRight) {
2017d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
2027d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
2037d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDisplay("360x360,700x700");
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DisplayController* controller =
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->display_controller();
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DisplayLayout layout(DisplayLayout::RIGHT, 0);
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ash::internal::MouseCursorEventFilter* event_filter =
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,16 1x344", event_filter->src_indicator_bounds_.ToString());
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,0 1x360", event_filter->dst_indicator_bounds_.ToString());
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,16 1x344", event_filter->src_indicator_bounds_.ToString());
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,0 1x360", event_filter->dst_indicator_bounds_.ToString());
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Move 2nd display downwards a bit.
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  layout.offset = 5;
222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is same as before because the 2nd display's y is above
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the indicator's x.
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,16 1x344", event_filter->src_indicator_bounds_.ToString());
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,5 1x355", event_filter->dst_indicator_bounds_.ToString());
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,21 1x339", event_filter->src_indicator_bounds_.ToString());
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,5 1x355", event_filter->dst_indicator_bounds_.ToString());
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Move it down further so that the shared edge is shorter than
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // minimum hole size (160).
234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  layout.offset = 200;
235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,200 1x160", event_filter->src_indicator_bounds_.ToString());
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,200 1x160", event_filter->dst_indicator_bounds_.ToString());
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,200 1x160", event_filter->src_indicator_bounds_.ToString());
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,200 1x160", event_filter->dst_indicator_bounds_.ToString());
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now move 2nd display upwards
244c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  layout.offset = -5;
245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,16 1x344", event_filter->src_indicator_bounds_.ToString());
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,0 1x360", event_filter->dst_indicator_bounds_.ToString());
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 16 px are reserved on 2nd display from top, so y must be
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (16 - 5) = 11
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("360,11 1x349", event_filter->src_indicator_bounds_.ToString());
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("359,0 1x360", event_filter->dst_indicator_bounds_.ToString());
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->HideSharedEdgeIndicator();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(MouseCursorEventFilterTest, IndicatorBoundsTestOnLeft) {
2597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
2607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
2617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDisplay("360x360,700x700");
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DisplayController* controller =
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->display_controller();
267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DisplayLayout layout(DisplayLayout::LEFT, 0);
268c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ash::internal::MouseCursorEventFilter* event_filter =
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,16 1x344", event_filter->src_indicator_bounds_.ToString());
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("-1,0 1x360", event_filter->dst_indicator_bounds_.ToString());
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("-1,16 1x344", event_filter->src_indicator_bounds_.ToString());
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,0 1x360", event_filter->dst_indicator_bounds_.ToString());
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  layout.offset = 250;
279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,250 1x110", event_filter->src_indicator_bounds_.ToString());
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("-1,250 1x110", event_filter->dst_indicator_bounds_.ToString());
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("-1,250 1x110", event_filter->src_indicator_bounds_.ToString());
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,250 1x110", event_filter->dst_indicator_bounds_.ToString());
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->HideSharedEdgeIndicator();
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(MouseCursorEventFilterTest, IndicatorBoundsTestOnTopBottom) {
2907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
2917d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
2927d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDisplay("360x360,700x700");
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DisplayController* controller =
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->display_controller();
298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DisplayLayout layout(DisplayLayout::TOP, 0);
299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ash::internal::MouseCursorEventFilter* event_filter =
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,0 360x1", event_filter->src_indicator_bounds_.ToString());
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,-1 360x1", event_filter->dst_indicator_bounds_.ToString());
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,-1 360x1", event_filter->src_indicator_bounds_.ToString());
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,0 360x1", event_filter->dst_indicator_bounds_.ToString());
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  layout.offset = 250;
310c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("250,0 110x1", event_filter->src_indicator_bounds_.ToString());
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("250,-1 110x1", event_filter->dst_indicator_bounds_.ToString());
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("250,-1 110x1", event_filter->src_indicator_bounds_.ToString());
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("250,0 110x1", event_filter->dst_indicator_bounds_.ToString());
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  layout.position = DisplayLayout::BOTTOM;
319c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  layout.offset = 0;
320c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(layout);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[0] /* primary */);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,359 360x1", event_filter->src_indicator_bounds_.ToString());
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,360 360x1", event_filter->dst_indicator_bounds_.ToString());
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->ShowSharedEdgeIndicator(root_windows[1] /* secondary */);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,360 360x1", event_filter->src_indicator_bounds_.ToString());
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("0,359 360x1", event_filter->dst_indicator_bounds_.ToString());
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->HideSharedEdgeIndicator();
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Verifies cursor's device scale factor is updated when a cursor has moved
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// across root windows with different device scale factors
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// (http://crbug.com/154183).
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(MouseCursorEventFilterTest, CursorDeviceScaleFactor) {
3357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!SupportsMultipleDisplays())
3367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    return;
3377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDisplay("400x400,800x800*2");
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DisplayController* controller =
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->display_controller();
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  controller->SetLayoutForCurrentDisplays(
342c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DisplayLayout(DisplayLayout::RIGHT, 0));
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(2U, root_windows.size());
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test::CursorManagerTestApi cursor_test_api(
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->cursor_manager());
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MouseCursorEventFilter* event_filter =
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Shell::GetInstance()->mouse_cursor_filter();
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1.0f, cursor_test_api.GetDisplay().device_scale_factor());
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->WarpMouseCursorIfNecessary(root_windows[0],
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           gfx::Point(399, 200));
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(2.0f, cursor_test_api.GetDisplay().device_scale_factor());
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  event_filter->WarpMouseCursorIfNecessary(root_windows[1],
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           gfx::Point(400, 200));
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_EQ(1.0f, cursor_test_api.GetDisplay().device_scale_factor());
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace internal
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ash
361