client_controlled_shell_surface_unittest.cc 102 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602
  1. // Copyright 2017 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. #include "components/exo/client_controlled_shell_surface.h"
  5. #include "ash/display/screen_orientation_controller.h"
  6. #include "ash/frame/header_view.h"
  7. #include "ash/frame/non_client_frame_view_ash.h"
  8. #include "ash/frame/wide_frame_view.h"
  9. #include "ash/public/cpp/arc_resize_lock_type.h"
  10. #include "ash/public/cpp/shelf_types.h"
  11. #include "ash/public/cpp/test/shell_test_api.h"
  12. #include "ash/shelf/shelf.h"
  13. #include "ash/shell.h"
  14. #include "ash/system/unified/unified_system_tray.h"
  15. #include "ash/test/test_widget_builder.h"
  16. #include "ash/wm/drag_window_resizer.h"
  17. #include "ash/wm/overview/overview_controller.h"
  18. #include "ash/wm/pip/pip_positioner.h"
  19. #include "ash/wm/splitview/split_view_controller.h"
  20. #include "ash/wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h"
  21. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  22. #include "ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h"
  23. #include "ash/wm/tablet_mode/tablet_mode_window_resizer.h"
  24. #include "ash/wm/window_positioning_utils.h"
  25. #include "ash/wm/window_resizer.h"
  26. #include "ash/wm/window_restore/window_restore_controller.h"
  27. #include "ash/wm/window_state.h"
  28. #include "ash/wm/window_util.h"
  29. #include "ash/wm/wm_event.h"
  30. #include "ash/wm/work_area_insets.h"
  31. #include "ash/wm/workspace_controller_test_api.h"
  32. #include "base/bind.h"
  33. #include "base/memory/ptr_util.h"
  34. #include "base/run_loop.h"
  35. #include "base/strings/utf_string_conversions.h"
  36. #include "base/test/scoped_feature_list.h"
  37. #include "cc/paint/display_item_list.h"
  38. #include "chromeos/ui/base/window_pin_type.h"
  39. #include "chromeos/ui/base/window_properties.h"
  40. #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
  41. #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
  42. #include "components/app_restore/full_restore_utils.h"
  43. #include "components/app_restore/window_properties.h"
  44. #include "components/exo/buffer.h"
  45. #include "components/exo/display.h"
  46. #include "components/exo/permission.h"
  47. #include "components/exo/pointer.h"
  48. #include "components/exo/shell_surface_util.h"
  49. #include "components/exo/sub_surface.h"
  50. #include "components/exo/surface.h"
  51. #include "components/exo/test/exo_test_base.h"
  52. #include "components/exo/test/exo_test_helper.h"
  53. #include "components/exo/test/shell_surface_builder.h"
  54. #include "components/exo/wm_helper.h"
  55. #include "third_party/skia/include/utils/SkNoDrawCanvas.h"
  56. #include "ui/aura/client/aura_constants.h"
  57. #include "ui/aura/client/window_parenting_client.h"
  58. #include "ui/aura/env.h"
  59. #include "ui/aura/window.h"
  60. #include "ui/aura/window_event_dispatcher.h"
  61. #include "ui/aura/window_targeter.h"
  62. #include "ui/aura/window_tree_host.h"
  63. #include "ui/compositor/layer.h"
  64. #include "ui/compositor/scoped_animation_duration_scale_mode.h"
  65. #include "ui/compositor/scoped_layer_animation_settings.h"
  66. #include "ui/compositor_extra/shadow.h"
  67. #include "ui/display/display.h"
  68. #include "ui/display/test/display_manager_test_api.h"
  69. #include "ui/display/util/display_util.h"
  70. #include "ui/events/base_event_utils.h"
  71. #include "ui/events/event_targeter.h"
  72. #include "ui/events/test/event_generator.h"
  73. #include "ui/gfx/geometry/insets.h"
  74. #include "ui/views/paint_info.h"
  75. #include "ui/views/widget/widget.h"
  76. #include "ui/wm/core/shadow_controller.h"
  77. #include "ui/wm/core/shadow_types.h"
  78. using chromeos::WindowStateType;
  79. namespace exo {
  80. namespace {
  81. using ClientControlledShellSurfaceTest = test::ExoTestBase;
  82. bool HasBackdrop() {
  83. ash::WorkspaceController* wc = ash::ShellTestApi().workspace_controller();
  84. return !!ash::WorkspaceControllerTestApi(wc).GetBackdropWindow();
  85. }
  86. bool IsWidgetPinned(views::Widget* widget) {
  87. return ash::WindowState::Get(widget->GetNativeWindow())->IsPinned();
  88. }
  89. int GetShadowElevation(aura::Window* window) {
  90. return window->GetProperty(wm::kShadowElevationKey);
  91. }
  92. void EnableTabletMode(bool enable) {
  93. ash::Shell::Get()->tablet_mode_controller()->SetEnabledForTest(enable);
  94. }
  95. // A canvas that just logs when a text blob is drawn.
  96. class TestCanvas : public SkNoDrawCanvas {
  97. public:
  98. TestCanvas() : SkNoDrawCanvas(100, 100) {}
  99. TestCanvas(const TestCanvas&) = delete;
  100. TestCanvas& operator=(const TestCanvas&) = delete;
  101. ~TestCanvas() override {}
  102. void onDrawTextBlob(const SkTextBlob*,
  103. SkScalar,
  104. SkScalar,
  105. const SkPaint&) override {
  106. text_was_drawn_ = true;
  107. }
  108. bool text_was_drawn() const { return text_was_drawn_; }
  109. private:
  110. bool text_was_drawn_ = false;
  111. };
  112. } // namespace
  113. TEST_F(ClientControlledShellSurfaceTest, SetPinned) {
  114. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  115. .BuildClientControlledShellSurface();
  116. shell_surface->SetPinned(chromeos::WindowPinType::kTrustedPinned);
  117. EXPECT_TRUE(IsWidgetPinned(shell_surface->GetWidget()));
  118. shell_surface->SetPinned(chromeos::WindowPinType::kNone);
  119. EXPECT_FALSE(IsWidgetPinned(shell_surface->GetWidget()));
  120. shell_surface->SetPinned(chromeos::WindowPinType::kPinned);
  121. EXPECT_TRUE(IsWidgetPinned(shell_surface->GetWidget()));
  122. shell_surface->SetPinned(chromeos::WindowPinType::kNone);
  123. EXPECT_FALSE(IsWidgetPinned(shell_surface->GetWidget()));
  124. }
  125. TEST_F(ClientControlledShellSurfaceTest, SetSystemUiVisibility) {
  126. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  127. .BuildClientControlledShellSurface();
  128. shell_surface->SetSystemUiVisibility(true);
  129. EXPECT_TRUE(
  130. ash::WindowState::Get(shell_surface->GetWidget()->GetNativeWindow())
  131. ->autohide_shelf_when_maximized_or_fullscreen());
  132. shell_surface->SetSystemUiVisibility(false);
  133. EXPECT_FALSE(
  134. ash::WindowState::Get(shell_surface->GetWidget()->GetNativeWindow())
  135. ->autohide_shelf_when_maximized_or_fullscreen());
  136. }
  137. TEST_F(ClientControlledShellSurfaceTest, SetTopInset) {
  138. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  139. .BuildClientControlledShellSurface();
  140. auto* surface = shell_surface->root_surface();
  141. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  142. ASSERT_TRUE(window);
  143. EXPECT_EQ(0, window->GetProperty(aura::client::kTopViewInset));
  144. int top_inset_height = 20;
  145. shell_surface->SetTopInset(top_inset_height);
  146. surface->Commit();
  147. EXPECT_EQ(top_inset_height, window->GetProperty(aura::client::kTopViewInset));
  148. }
  149. TEST_F(ClientControlledShellSurfaceTest, UpdateModalWindow) {
  150. auto shell_surface = exo::test::ShellSurfaceBuilder({640, 480})
  151. .SetUseSystemModalContainer()
  152. .SetInputRegion(cc::Region())
  153. .BuildClientControlledShellSurface();
  154. auto* surface = shell_surface->root_surface();
  155. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  156. EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
  157. // Creating a surface without input region should not make it modal.
  158. std::unique_ptr<Display> display(new Display);
  159. std::unique_ptr<Surface> child = display->CreateSurface();
  160. gfx::Size buffer_size(128, 128);
  161. std::unique_ptr<Buffer> child_buffer(
  162. new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
  163. child->Attach(child_buffer.get());
  164. std::unique_ptr<SubSurface> sub_surface(
  165. display->CreateSubSurface(child.get(), surface));
  166. surface->SetSubSurfacePosition(child.get(), gfx::PointF(10, 10));
  167. child->Commit();
  168. surface->Commit();
  169. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  170. EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
  171. // Making the surface opaque shouldn't make it modal either.
  172. child->SetBlendMode(SkBlendMode::kSrc);
  173. child->Commit();
  174. surface->Commit();
  175. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  176. EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
  177. // Setting input regions won't make it modal either.
  178. surface->SetInputRegion(gfx::Rect(10, 10, 100, 100));
  179. surface->Commit();
  180. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  181. EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
  182. // Only SetSystemModal changes modality.
  183. shell_surface->SetSystemModal(true);
  184. EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen());
  185. EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
  186. shell_surface->SetSystemModal(false);
  187. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  188. EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
  189. // If the non modal system window was active,
  190. shell_surface->GetWidget()->Activate();
  191. EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
  192. shell_surface->SetSystemModal(true);
  193. EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen());
  194. EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
  195. shell_surface->SetSystemModal(false);
  196. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  197. EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
  198. }
  199. TEST_F(ClientControlledShellSurfaceTest,
  200. ModalWindowSetSystemModalBeforeCommit) {
  201. auto shell_surface = exo::test::ShellSurfaceBuilder({640, 480})
  202. .SetUseSystemModalContainer()
  203. .SetInputRegion(cc::Region())
  204. .SetNoCommit()
  205. .BuildClientControlledShellSurface();
  206. auto* surface = shell_surface->root_surface();
  207. // Set SetSystemModal before any commit happens. Widget is not created at
  208. // this time.
  209. EXPECT_FALSE(shell_surface->GetWidget());
  210. shell_surface->SetSystemModal(true);
  211. surface->Commit();
  212. // It is expected that modal window is shown.
  213. EXPECT_TRUE(shell_surface->GetWidget());
  214. EXPECT_TRUE(ash::Shell::IsSystemModalWindowOpen());
  215. // Now widget is created and setting modal state should be applied
  216. // immediately.
  217. shell_surface->SetSystemModal(false);
  218. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  219. }
  220. TEST_F(ClientControlledShellSurfaceTest,
  221. NonSystemModalContainerCantChangeModality) {
  222. auto shell_surface = exo::test::ShellSurfaceBuilder({640, 480})
  223. .SetInputRegion(cc::Region())
  224. .EnableSystemModal()
  225. .BuildClientControlledShellSurface();
  226. // It is expected that a non system modal container is unable to set a system
  227. // modal.
  228. EXPECT_FALSE(ash::Shell::IsSystemModalWindowOpen());
  229. }
  230. TEST_F(ClientControlledShellSurfaceTest, SurfaceShadow) {
  231. auto shell_surface = exo::test::ShellSurfaceBuilder({128, 128})
  232. .BuildClientControlledShellSurface();
  233. auto* surface = shell_surface->root_surface();
  234. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  235. // 1) Initial state, no shadow (SurfaceFrameType is NONE);
  236. EXPECT_FALSE(wm::ShadowController::GetShadowForWindow(window));
  237. std::unique_ptr<Display> display(new Display);
  238. // 2) Just creating a sub surface won't create a shadow.
  239. auto* child =
  240. test::ShellSurfaceBuilder::AddChildSurface(surface, {0, 0, 128, 128});
  241. surface->Commit();
  242. EXPECT_FALSE(wm::ShadowController::GetShadowForWindow(window));
  243. // 3) Create a shadow.
  244. surface->SetFrame(SurfaceFrameType::SHADOW);
  245. shell_surface->SetShadowBounds(gfx::Rect(10, 10, 100, 100));
  246. surface->Commit();
  247. ui::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window);
  248. ASSERT_TRUE(shadow);
  249. EXPECT_TRUE(shadow->layer()->visible());
  250. gfx::Rect before = shadow->layer()->bounds();
  251. // 4) Shadow bounds is independent of the sub surface.
  252. gfx::Size new_buffer_size(256, 256);
  253. std::unique_ptr<Buffer> new_child_buffer(
  254. new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(new_buffer_size)));
  255. child->Attach(new_child_buffer.get());
  256. child->Commit();
  257. surface->Commit();
  258. EXPECT_EQ(before, shadow->layer()->bounds());
  259. // 4) Updating the widget's window bounds should not change the shadow bounds.
  260. // TODO(oshima): The following scenario only worked with Xdg/ShellSurface,
  261. // which never uses SetShadowBounds. This is broken with correct scenario, and
  262. // will be fixed when the bounds control is delegated to the client.
  263. //
  264. // window->SetBounds(gfx::Rect(10, 10, 100, 100));
  265. // EXPECT_EQ(before, shadow->layer()->bounds());
  266. // 5) This should disable shadow.
  267. shell_surface->SetShadowBounds(gfx::Rect());
  268. surface->Commit();
  269. EXPECT_EQ(wm::kShadowElevationNone, GetShadowElevation(window));
  270. EXPECT_FALSE(shadow->layer()->visible());
  271. // 6) This should enable non surface shadow again.
  272. shell_surface->SetShadowBounds(gfx::Rect(10, 10, 100, 100));
  273. surface->Commit();
  274. EXPECT_EQ(wm::kShadowElevationDefault, GetShadowElevation(window));
  275. EXPECT_TRUE(shadow->layer()->visible());
  276. }
  277. TEST_F(ClientControlledShellSurfaceTest, ShadowWithStateChange) {
  278. const gfx::Size content_size(100, 100);
  279. // Position the widget at 10,10 so that we get non zero offset.
  280. auto shell_surface = exo::test::ShellSurfaceBuilder(content_size)
  281. .SetGeometry({gfx::Point(10, 10), content_size})
  282. .SetFrame(SurfaceFrameType::SHADOW)
  283. .BuildClientControlledShellSurface();
  284. auto* surface = shell_surface->root_surface();
  285. // In parent coordinates.
  286. const gfx::Rect shadow_bounds(gfx::Point(-10, -10), content_size);
  287. views::Widget* widget = shell_surface->GetWidget();
  288. aura::Window* window = widget->GetNativeWindow();
  289. ui::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window);
  290. shell_surface->SetShadowBounds(shadow_bounds);
  291. surface->Commit();
  292. EXPECT_EQ(wm::kShadowElevationDefault, GetShadowElevation(window));
  293. EXPECT_TRUE(shadow->layer()->visible());
  294. // Origin must be in sync.
  295. EXPECT_EQ(shadow_bounds.origin(), shadow->content_bounds().origin());
  296. const gfx::Rect work_area =
  297. display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  298. // Maximizing window hides the shadow.
  299. widget->Maximize();
  300. ASSERT_TRUE(widget->IsMaximized());
  301. EXPECT_FALSE(shadow->layer()->visible());
  302. shell_surface->SetShadowBounds(work_area);
  303. surface->Commit();
  304. EXPECT_FALSE(shadow->layer()->visible());
  305. // Restoring bounds will re-enable shadow. It's content size is set to work
  306. // area,/ thus not visible until new bounds is committed.
  307. widget->Restore();
  308. EXPECT_TRUE(shadow->layer()->visible());
  309. EXPECT_EQ(work_area, shadow->content_bounds());
  310. // The bounds is updated.
  311. shell_surface->SetShadowBounds(shadow_bounds);
  312. surface->Commit();
  313. EXPECT_EQ(shadow_bounds, shadow->content_bounds());
  314. }
  315. TEST_F(ClientControlledShellSurfaceTest, ShadowWithTransform) {
  316. const gfx::Size content_size(100, 100);
  317. // Position the widget at 10,10 so that we get non zero offset.
  318. auto shell_surface = exo::test::ShellSurfaceBuilder(content_size)
  319. .SetGeometry({gfx::Point(10, 10), content_size})
  320. .SetFrame(SurfaceFrameType::SHADOW)
  321. .BuildClientControlledShellSurface();
  322. auto* surface = shell_surface->root_surface();
  323. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  324. ui::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window);
  325. // In parent coordinates.
  326. const gfx::Rect shadow_bounds(gfx::Point(-10, -10), content_size);
  327. // Shadow bounds relative to its parent should not be affected by a transform.
  328. gfx::Transform transform;
  329. transform.Translate(50, 50);
  330. window->SetTransform(transform);
  331. shell_surface->SetShadowBounds(shadow_bounds);
  332. surface->Commit();
  333. EXPECT_TRUE(shadow->layer()->visible());
  334. EXPECT_EQ(gfx::Rect(-10, -10, 100, 100), shadow->content_bounds());
  335. }
  336. TEST_F(ClientControlledShellSurfaceTest, ShadowStartMaximized) {
  337. auto shell_surface =
  338. exo::test::ShellSurfaceBuilder({256, 256})
  339. .SetWindowState(chromeos::WindowStateType::kMaximized)
  340. .SetFrame(SurfaceFrameType::SHADOW)
  341. .BuildClientControlledShellSurface();
  342. auto* surface = shell_surface->root_surface();
  343. views::Widget* widget = shell_surface->GetWidget();
  344. aura::Window* window = widget->GetNativeWindow();
  345. // There is no shadow when started in maximized state.
  346. EXPECT_FALSE(wm::ShadowController::GetShadowForWindow(window));
  347. // Sending a shadow bounds in maximized state won't create a shadow.
  348. shell_surface->SetShadowBounds(gfx::Rect(10, 10, 100, 100));
  349. surface->Commit();
  350. EXPECT_FALSE(wm::ShadowController::GetShadowForWindow(window));
  351. // Restore the window and make sure the shadow is created, visible and
  352. // has the latest bounds.
  353. widget->Restore();
  354. ui::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window);
  355. ASSERT_TRUE(shadow);
  356. EXPECT_TRUE(shadow->layer()->visible());
  357. EXPECT_EQ(gfx::Rect(10, 10, 100, 100), shadow->content_bounds());
  358. }
  359. TEST_F(ClientControlledShellSurfaceTest, Frame) {
  360. UpdateDisplay("800x600");
  361. gfx::Rect client_bounds(20, 50, 300, 200);
  362. gfx::Rect fullscreen_bounds(0, 0, 800, 600);
  363. // The window bounds is the client bounds + frame size.
  364. gfx::Rect normal_window_bounds(20, 18, 300, 232);
  365. auto shell_surface = exo::test::ShellSurfaceBuilder({client_bounds.size()})
  366. .SetGeometry(client_bounds)
  367. .SetFrame(SurfaceFrameType::NORMAL)
  368. .SetNoCommit()
  369. .BuildClientControlledShellSurface();
  370. auto* surface = shell_surface->root_surface();
  371. shell_surface->SetSystemUiVisibility(true); // disable shelf.
  372. surface->Commit();
  373. int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
  374. display::DisplayManager* display_manager =
  375. ash::Shell::Get()->display_manager();
  376. views::Widget* widget = shell_surface->GetWidget();
  377. ash::NonClientFrameViewAsh* frame_view =
  378. static_cast<ash::NonClientFrameViewAsh*>(
  379. widget->non_client_view()->frame_view());
  380. // Normal state.
  381. widget->LayoutRootViewIfNecessary();
  382. EXPECT_TRUE(frame_view->GetFrameEnabled());
  383. EXPECT_EQ(normal_window_bounds, widget->GetWindowBoundsInScreen());
  384. EXPECT_EQ(client_bounds,
  385. frame_view->GetClientBoundsForWindowBounds(normal_window_bounds));
  386. // Maximized
  387. shell_surface->SetMaximized();
  388. shell_surface->SetGeometry(gfx::Rect(0, 0, 800, 568));
  389. surface->Commit();
  390. widget->LayoutRootViewIfNecessary();
  391. EXPECT_TRUE(frame_view->GetFrameEnabled());
  392. EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen());
  393. EXPECT_EQ(
  394. gfx::Size(800, 568),
  395. frame_view->GetClientBoundsForWindowBounds(fullscreen_bounds).size());
  396. // With work area top insets.
  397. display_manager->UpdateWorkAreaOfDisplay(display_id,
  398. gfx::Insets::TLBR(200, 0, 0, 0));
  399. shell_surface->SetGeometry(gfx::Rect(0, 0, 800, 368));
  400. surface->Commit();
  401. widget->LayoutRootViewIfNecessary();
  402. EXPECT_TRUE(frame_view->GetFrameEnabled());
  403. EXPECT_EQ(gfx::Rect(0, 200, 800, 400), widget->GetWindowBoundsInScreen());
  404. display_manager->UpdateWorkAreaOfDisplay(display_id, gfx::Insets());
  405. // AutoHide
  406. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  407. shell_surface->SetGeometry(fullscreen_bounds);
  408. surface->Commit();
  409. widget->LayoutRootViewIfNecessary();
  410. EXPECT_TRUE(frame_view->GetFrameEnabled());
  411. EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen());
  412. EXPECT_EQ(fullscreen_bounds,
  413. frame_view->GetClientBoundsForWindowBounds(fullscreen_bounds));
  414. // Fullscreen state.
  415. shell_surface->SetFullscreen(true);
  416. surface->Commit();
  417. widget->LayoutRootViewIfNecessary();
  418. EXPECT_TRUE(frame_view->GetFrameEnabled());
  419. EXPECT_EQ(fullscreen_bounds, widget->GetWindowBoundsInScreen());
  420. EXPECT_EQ(fullscreen_bounds,
  421. frame_view->GetClientBoundsForWindowBounds(fullscreen_bounds));
  422. // Updating frame, then window state should still update the frame state.
  423. surface->SetFrame(SurfaceFrameType::NORMAL);
  424. surface->Commit();
  425. widget->LayoutRootViewIfNecessary();
  426. EXPECT_FALSE(frame_view->GetHeaderView()->GetVisible());
  427. shell_surface->SetMaximized();
  428. surface->Commit();
  429. widget->LayoutRootViewIfNecessary();
  430. EXPECT_TRUE(frame_view->GetHeaderView()->GetVisible());
  431. // Restore to normal state.
  432. shell_surface->SetRestored();
  433. shell_surface->SetGeometry(client_bounds);
  434. surface->SetFrame(SurfaceFrameType::NORMAL);
  435. surface->Commit();
  436. widget->LayoutRootViewIfNecessary();
  437. EXPECT_TRUE(frame_view->GetFrameEnabled());
  438. EXPECT_EQ(normal_window_bounds, widget->GetWindowBoundsInScreen());
  439. EXPECT_EQ(client_bounds,
  440. frame_view->GetClientBoundsForWindowBounds(normal_window_bounds));
  441. // No frame. The all bounds are same as client bounds.
  442. shell_surface->SetRestored();
  443. shell_surface->SetGeometry(client_bounds);
  444. surface->SetFrame(SurfaceFrameType::NONE);
  445. surface->Commit();
  446. widget->LayoutRootViewIfNecessary();
  447. EXPECT_FALSE(frame_view->GetFrameEnabled());
  448. EXPECT_EQ(client_bounds, widget->GetWindowBoundsInScreen());
  449. EXPECT_EQ(client_bounds,
  450. frame_view->GetClientBoundsForWindowBounds(client_bounds));
  451. // Test NONE -> AUTOHIDE -> NONE
  452. shell_surface->SetMaximized();
  453. shell_surface->SetGeometry(fullscreen_bounds);
  454. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  455. surface->Commit();
  456. widget->LayoutRootViewIfNecessary();
  457. EXPECT_TRUE(frame_view->GetFrameEnabled());
  458. EXPECT_TRUE(frame_view->GetHeaderView()->in_immersive_mode());
  459. surface->SetFrame(SurfaceFrameType::NONE);
  460. surface->Commit();
  461. widget->LayoutRootViewIfNecessary();
  462. EXPECT_FALSE(frame_view->GetFrameEnabled());
  463. EXPECT_FALSE(frame_view->GetHeaderView()->in_immersive_mode());
  464. }
  465. namespace {
  466. class TestEventHandler : public ui::EventHandler {
  467. public:
  468. TestEventHandler() = default;
  469. TestEventHandler(const TestEventHandler&) = delete;
  470. TestEventHandler& operator=(const TestEventHandler&) = delete;
  471. ~TestEventHandler() override = default;
  472. // ui::EventHandler:
  473. void OnMouseEvent(ui::MouseEvent* event) override {
  474. mouse_events_.push_back(*event);
  475. }
  476. const std::vector<ui::MouseEvent>& mouse_events() const {
  477. return mouse_events_;
  478. }
  479. private:
  480. std::vector<ui::MouseEvent> mouse_events_;
  481. };
  482. } // namespace
  483. TEST_F(ClientControlledShellSurfaceTest, NoSynthesizedEventOnFrameChange) {
  484. UpdateDisplay("800x600");
  485. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  486. .SetWindowState(chromeos::WindowStateType::kNormal)
  487. .SetFrame(SurfaceFrameType::NORMAL)
  488. .BuildClientControlledShellSurface();
  489. auto* surface = shell_surface->root_surface();
  490. // Maximized
  491. gfx::Rect fullscreen_bounds(0, 0, 800, 600);
  492. shell_surface->SetMaximized();
  493. shell_surface->SetGeometry(fullscreen_bounds);
  494. surface->Commit();
  495. // AutoHide
  496. base::RunLoop().RunUntilIdle();
  497. aura::Env* env = aura::Env::GetInstance();
  498. gfx::Rect cropped_fullscreen_bounds(0, 0, 800, 400);
  499. env->SetLastMouseLocation(gfx::Point(100, 30));
  500. TestEventHandler handler;
  501. env->AddPreTargetHandler(&handler);
  502. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  503. shell_surface->SetGeometry(cropped_fullscreen_bounds);
  504. surface->Commit();
  505. base::RunLoop().RunUntilIdle();
  506. EXPECT_TRUE(handler.mouse_events().empty());
  507. env->RemovePreTargetHandler(&handler);
  508. }
  509. // Shell surfaces should not emit extra events on commit even if using pixel
  510. // coordinates and a cursor is hovering over the window.
  511. // https://crbug.com/1296315.
  512. TEST_F(ClientControlledShellSurfaceTest,
  513. NoSynthesizedEventsForPixelCoordinates) {
  514. TestEventHandler event_handler;
  515. auto shell_surface = exo::test::ShellSurfaceBuilder({400, 400})
  516. .SetNoCommit()
  517. .BuildClientControlledShellSurface();
  518. auto* surface = shell_surface->root_surface();
  519. // Pixel coordinates add a transform to the underlying layer.
  520. shell_surface->set_client_submits_surfaces_in_pixel_coordinates(true);
  521. display::Display primary_display =
  522. display::Screen::GetScreen()->GetPrimaryDisplay();
  523. gfx::Rect initial_bounds(150, 10, 200, 200);
  524. shell_surface->SetBounds(primary_display.id(), initial_bounds);
  525. // Tested condition only happens when cursor is over the window.
  526. ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow());
  527. generator.MoveMouseTo(200, 110);
  528. shell_surface->host_window()->AddPreTargetHandler(&event_handler);
  529. shell_surface->Activate();
  530. // Commit an arbitrary number of frames. We expect that this will not generate
  531. // synthetic events.
  532. for (int i = 0; i < 5; i++) {
  533. surface->Commit();
  534. task_environment()->RunUntilIdle();
  535. }
  536. // There should be 2 events. One for mouse enter and the other for move.
  537. const auto& events = event_handler.mouse_events();
  538. ASSERT_EQ(events.size(), 2UL);
  539. EXPECT_EQ(events[0].type(), ui::ET_MOUSE_ENTERED);
  540. EXPECT_EQ(events[1].type(), ui::ET_MOUSE_MOVED);
  541. shell_surface->host_window()->RemovePreTargetHandler(&event_handler);
  542. }
  543. TEST_F(ClientControlledShellSurfaceTest, CompositorLockInRotation) {
  544. UpdateDisplay("800x600");
  545. ash::Shell* shell = ash::Shell::Get();
  546. shell->tablet_mode_controller()->SetEnabledForTest(true);
  547. gfx::Rect maximum_bounds =
  548. display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
  549. // Start in maximized.
  550. auto shell_surface =
  551. exo::test::ShellSurfaceBuilder({800, 600})
  552. .SetWindowState(chromeos::WindowStateType::kMaximized)
  553. .SetGeometry(maximum_bounds)
  554. .SetNoCommit()
  555. .BuildClientControlledShellSurface();
  556. shell_surface->SetOrientation(Orientation::LANDSCAPE);
  557. auto* surface = shell_surface->root_surface();
  558. surface->Commit();
  559. ui::Compositor* compositor =
  560. shell_surface->GetWidget()->GetNativeWindow()->layer()->GetCompositor();
  561. EXPECT_FALSE(compositor->IsLocked());
  562. UpdateDisplay("800x600/r");
  563. EXPECT_TRUE(compositor->IsLocked());
  564. shell_surface->SetOrientation(Orientation::PORTRAIT);
  565. surface->Commit();
  566. shell_surface->DidReceiveCompositorFrameAck();
  567. EXPECT_FALSE(compositor->IsLocked());
  568. }
  569. // If system tray is shown by click. It should be activated if user presses tab
  570. // key while shell surface is active.
  571. TEST_F(ClientControlledShellSurfaceTest,
  572. KeyboardNavigationWithUnifiedSystemTray) {
  573. auto shell_surface = exo::test::ShellSurfaceBuilder({800, 600})
  574. .BuildClientControlledShellSurface();
  575. EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
  576. // Show system tray by performing a gesture tap at tray.
  577. ash::UnifiedSystemTray* system_tray = GetPrimaryUnifiedSystemTray();
  578. ui::GestureEvent tap(0, 0, 0, base::TimeTicks(),
  579. ui::GestureEventDetails(ui::ET_GESTURE_TAP));
  580. system_tray->PerformAction(tap);
  581. ASSERT_TRUE(system_tray->GetWidget());
  582. // Confirm that system tray is not active at this time.
  583. EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
  584. EXPECT_FALSE(system_tray->IsBubbleActive());
  585. // Send tab key event.
  586. PressAndReleaseKey(ui::VKEY_TAB);
  587. // Confirm that system tray is activated.
  588. EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
  589. EXPECT_TRUE(system_tray->IsBubbleActive());
  590. }
  591. TEST_F(ClientControlledShellSurfaceTest, Maximize) {
  592. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  593. .BuildClientControlledShellSurface();
  594. auto* surface = shell_surface->root_surface();
  595. EXPECT_FALSE(HasBackdrop());
  596. shell_surface->SetMaximized();
  597. EXPECT_FALSE(HasBackdrop());
  598. surface->Commit();
  599. EXPECT_TRUE(HasBackdrop());
  600. EXPECT_TRUE(shell_surface->GetWidget()->IsMaximized());
  601. // We always show backdrop because the window may be cropped.
  602. display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
  603. shell_surface->SetGeometry(display.bounds());
  604. surface->Commit();
  605. EXPECT_TRUE(HasBackdrop());
  606. shell_surface->SetGeometry(gfx::Rect(0, 0, 100, display.bounds().height()));
  607. surface->Commit();
  608. EXPECT_TRUE(HasBackdrop());
  609. shell_surface->SetGeometry(gfx::Rect(0, 0, display.bounds().width(), 100));
  610. surface->Commit();
  611. EXPECT_TRUE(HasBackdrop());
  612. // Toggle maximize.
  613. ash::WMEvent maximize_event(ash::WM_EVENT_TOGGLE_MAXIMIZE);
  614. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  615. ash::WindowState::Get(window)->OnWMEvent(&maximize_event);
  616. EXPECT_FALSE(shell_surface->GetWidget()->IsMaximized());
  617. EXPECT_FALSE(HasBackdrop());
  618. ash::WindowState::Get(window)->OnWMEvent(&maximize_event);
  619. EXPECT_TRUE(shell_surface->GetWidget()->IsMaximized());
  620. EXPECT_TRUE(HasBackdrop());
  621. }
  622. TEST_F(ClientControlledShellSurfaceTest, Restore) {
  623. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  624. .BuildClientControlledShellSurface();
  625. auto* surface = shell_surface->root_surface();
  626. EXPECT_FALSE(HasBackdrop());
  627. // Note: Remove contents to avoid issues with maximize animations in tests.
  628. shell_surface->SetMaximized();
  629. EXPECT_FALSE(HasBackdrop());
  630. surface->Commit();
  631. EXPECT_TRUE(HasBackdrop());
  632. shell_surface->SetRestored();
  633. EXPECT_TRUE(HasBackdrop());
  634. surface->Commit();
  635. EXPECT_FALSE(HasBackdrop());
  636. }
  637. TEST_F(ClientControlledShellSurfaceTest, SetFullscreen) {
  638. auto shell_surface =
  639. exo::test::ShellSurfaceBuilder({256, 256})
  640. .SetWindowState(chromeos::WindowStateType::kFullscreen)
  641. .BuildClientControlledShellSurface();
  642. auto* surface = shell_surface->root_surface();
  643. EXPECT_TRUE(HasBackdrop());
  644. // We always show backdrop becaues the window can be cropped.
  645. display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
  646. shell_surface->SetGeometry(display.bounds());
  647. surface->Commit();
  648. EXPECT_TRUE(HasBackdrop());
  649. shell_surface->SetGeometry(gfx::Rect(0, 0, 100, display.bounds().height()));
  650. surface->Commit();
  651. EXPECT_TRUE(HasBackdrop());
  652. shell_surface->SetGeometry(gfx::Rect(0, 0, display.bounds().width(), 100));
  653. surface->Commit();
  654. EXPECT_TRUE(HasBackdrop());
  655. shell_surface->SetFullscreen(false);
  656. surface->Commit();
  657. EXPECT_FALSE(HasBackdrop());
  658. EXPECT_NE(GetContext()->bounds().ToString(),
  659. shell_surface->GetWidget()->GetWindowBoundsInScreen().ToString());
  660. }
  661. TEST_F(ClientControlledShellSurfaceTest, ToggleFullscreen) {
  662. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  663. .BuildClientControlledShellSurface();
  664. auto* surface = shell_surface->root_surface();
  665. EXPECT_FALSE(HasBackdrop());
  666. shell_surface->SetMaximized();
  667. surface->Commit();
  668. EXPECT_TRUE(HasBackdrop());
  669. ash::WMEvent event(ash::WM_EVENT_TOGGLE_FULLSCREEN);
  670. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  671. // Enter fullscreen mode.
  672. ash::WindowState::Get(window)->OnWMEvent(&event);
  673. EXPECT_TRUE(HasBackdrop());
  674. // Leave fullscreen mode.
  675. ash::WindowState::Get(window)->OnWMEvent(&event);
  676. EXPECT_TRUE(HasBackdrop());
  677. }
  678. TEST_F(ClientControlledShellSurfaceTest,
  679. DefaultDeviceScaleFactorForcedScaleFactor) {
  680. double scale = 1.5;
  681. display::Display::SetForceDeviceScaleFactor(scale);
  682. int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
  683. display::SetInternalDisplayIds({display_id});
  684. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  685. .EnableDefaultScaleCancellation()
  686. .BuildClientControlledShellSurface();
  687. gfx::Transform transform;
  688. transform.Scale(1.0 / scale, 1.0 / scale);
  689. EXPECT_EQ(
  690. transform.ToString(),
  691. shell_surface->host_window()->layer()->GetTargetTransform().ToString());
  692. }
  693. TEST_F(ClientControlledShellSurfaceTest,
  694. DefaultDeviceScaleFactorFromDisplayManager) {
  695. int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
  696. display::SetInternalDisplayIds({display_id});
  697. gfx::Size size(1920, 1080);
  698. display::DisplayManager* display_manager =
  699. ash::Shell::Get()->display_manager();
  700. double scale = 1.25;
  701. display::ManagedDisplayMode mode(size, 60.f, false /* overscan */,
  702. true /*native*/, scale);
  703. display::ManagedDisplayInfo::ManagedDisplayModeList mode_list;
  704. mode_list.push_back(mode);
  705. display::ManagedDisplayInfo native_display_info(display_id, "test", false);
  706. native_display_info.SetManagedDisplayModes(mode_list);
  707. native_display_info.SetBounds(gfx::Rect(size));
  708. native_display_info.set_device_scale_factor(scale);
  709. std::vector<display::ManagedDisplayInfo> display_info_list;
  710. display_info_list.push_back(native_display_info);
  711. display_manager->OnNativeDisplaysChanged(display_info_list);
  712. display_manager->UpdateInternalManagedDisplayModeListForTest();
  713. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  714. .BuildClientControlledShellSurface();
  715. gfx::Transform transform;
  716. transform.Scale(1.0 / scale, 1.0 / scale);
  717. EXPECT_EQ(
  718. transform.ToString(),
  719. shell_surface->host_window()->layer()->GetTargetTransform().ToString());
  720. }
  721. TEST_F(ClientControlledShellSurfaceTest, MouseAndTouchTarget) {
  722. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  723. .SetGeometry({0, 0, 256, 256})
  724. .BuildClientControlledShellSurface();
  725. EXPECT_TRUE(shell_surface->CanResize());
  726. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  727. aura::Window* root = window->GetRootWindow();
  728. ui::EventTargeter* targeter =
  729. root->GetHost()->dispatcher()->GetDefaultEventTargeter();
  730. gfx::Point mouse_location(256 + 5, 150);
  731. ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
  732. ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
  733. EXPECT_EQ(window, targeter->FindTargetForEvent(root, &mouse));
  734. // Move 20px further away. Touch event can hit the window but
  735. // mouse event will not.
  736. gfx::Point touch_location(256 + 25, 150);
  737. ui::MouseEvent touch(ui::ET_TOUCH_PRESSED, touch_location, touch_location,
  738. ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
  739. EXPECT_EQ(window, targeter->FindTargetForEvent(root, &touch));
  740. ui::MouseEvent mouse_with_touch_loc(ui::ET_MOUSE_MOVED, touch_location,
  741. touch_location, ui::EventTimeForNow(),
  742. ui::EF_NONE, ui::EF_NONE);
  743. EXPECT_FALSE(window->Contains(static_cast<aura::Window*>(
  744. targeter->FindTargetForEvent(root, &mouse_with_touch_loc))));
  745. // Touching futher away shouldn't hit the window.
  746. gfx::Point no_touch_location(256 + 35, 150);
  747. ui::MouseEvent no_touch(ui::ET_TOUCH_PRESSED, no_touch_location,
  748. no_touch_location, ui::EventTimeForNow(), ui::EF_NONE,
  749. ui::EF_NONE);
  750. EXPECT_FALSE(window->Contains(static_cast<aura::Window*>(
  751. targeter->FindTargetForEvent(root, &no_touch))));
  752. }
  753. // The shell surface in SystemModal container should be unresizable.
  754. TEST_F(ClientControlledShellSurfaceTest,
  755. ShellSurfaceInSystemModalIsUnresizable) {
  756. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  757. .SetUseSystemModalContainer()
  758. .BuildClientControlledShellSurface();
  759. EXPECT_FALSE(shell_surface->GetWidget()->widget_delegate()->CanResize());
  760. }
  761. // The shell surface in SystemModal container should be a target
  762. // at the edge.
  763. TEST_F(ClientControlledShellSurfaceTest, ShellSurfaceInSystemModalHitTest) {
  764. display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
  765. auto shell_surface = exo::test::ShellSurfaceBuilder({640, 480})
  766. .SetUseSystemModalContainer()
  767. .SetGeometry(display.bounds())
  768. .SetInputRegion(gfx::Rect(0, 0, 0, 0))
  769. .BuildClientControlledShellSurface();
  770. EXPECT_FALSE(shell_surface->GetWidget()->widget_delegate()->CanResize());
  771. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  772. aura::Window* root = window->GetRootWindow();
  773. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(100, 0),
  774. gfx::Point(100, 0), ui::EventTimeForNow(), 0, 0);
  775. aura::WindowTargeter targeter;
  776. aura::Window* found =
  777. static_cast<aura::Window*>(targeter.FindTargetForEvent(root, &event));
  778. EXPECT_TRUE(window->Contains(found));
  779. }
  780. // Test the snap functionalities in splitscreen in tablet mode.
  781. TEST_F(ClientControlledShellSurfaceTest, SnapWindowInSplitViewModeTest) {
  782. UpdateDisplay("807x607");
  783. ash::Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  784. auto shell_surface1 =
  785. exo::test::ShellSurfaceBuilder({800, 600})
  786. .SetGeometry({0, 0, 800, 600})
  787. .SetWindowState(chromeos::WindowStateType::kMaximized)
  788. .BuildClientControlledShellSurface();
  789. aura::Window* window1 = shell_surface1->GetWidget()->GetNativeWindow();
  790. ash::WindowState* window_state1 = ash::WindowState::Get(window1);
  791. ash::ClientControlledState* state1 = static_cast<ash::ClientControlledState*>(
  792. ash::WindowState::TestApi::GetStateImpl(window_state1));
  793. EXPECT_EQ(window_state1->GetStateType(), WindowStateType::kMaximized);
  794. // Snap window to left.
  795. ash::SplitViewController* split_view_controller =
  796. ash::SplitViewController::Get(ash::Shell::GetPrimaryRootWindow());
  797. split_view_controller->SnapWindow(window1, ash::SplitViewController::LEFT);
  798. state1->set_bounds_locally(true);
  799. window1->SetBounds(split_view_controller->GetSnappedWindowBoundsInScreen(
  800. ash::SplitViewController::LEFT, window1));
  801. state1->set_bounds_locally(false);
  802. EXPECT_EQ(window_state1->GetStateType(), WindowStateType::kPrimarySnapped);
  803. EXPECT_EQ(shell_surface1->GetWidget()->GetWindowBoundsInScreen(),
  804. split_view_controller->GetSnappedWindowBoundsInScreen(
  805. ash::SplitViewController::LEFT,
  806. shell_surface1->GetWidget()->GetNativeWindow()));
  807. EXPECT_TRUE(HasBackdrop());
  808. split_view_controller->EndSplitView();
  809. // Snap window to right.
  810. split_view_controller->SnapWindow(window1, ash::SplitViewController::RIGHT);
  811. state1->set_bounds_locally(true);
  812. window1->SetBounds(split_view_controller->GetSnappedWindowBoundsInScreen(
  813. ash::SplitViewController::RIGHT, window1));
  814. state1->set_bounds_locally(false);
  815. EXPECT_EQ(window_state1->GetStateType(), WindowStateType::kSecondarySnapped);
  816. EXPECT_EQ(shell_surface1->GetWidget()->GetWindowBoundsInScreen(),
  817. split_view_controller->GetSnappedWindowBoundsInScreen(
  818. ash::SplitViewController::RIGHT,
  819. shell_surface1->GetWidget()->GetNativeWindow()));
  820. EXPECT_TRUE(HasBackdrop());
  821. }
  822. // The shell surface in SystemModal container should not become target
  823. // at the edge.
  824. TEST_F(ClientControlledShellSurfaceTest, ClientIniatedResize) {
  825. display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
  826. auto shell_surface = exo::test::ShellSurfaceBuilder({100, 100})
  827. .SetGeometry(gfx::Rect({0, 0, 100, 100}))
  828. .BuildClientControlledShellSurface();
  829. EXPECT_TRUE(shell_surface->GetWidget()->widget_delegate()->CanResize());
  830. shell_surface->StartDrag(HTTOP, gfx::PointF(0, 0));
  831. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  832. // Client cannot start drag if mouse isn't pressed.
  833. ash::WindowState* window_state = ash::WindowState::Get(window);
  834. ASSERT_FALSE(window_state->is_dragged());
  835. // Client can start drag only when the mouse is pressed on the widget.
  836. ui::test::EventGenerator* event_generator = GetEventGenerator();
  837. event_generator->MoveMouseToCenterOf(window);
  838. event_generator->PressLeftButton();
  839. shell_surface->StartDrag(HTTOP, gfx::PointF(0, 0));
  840. ASSERT_TRUE(window_state->is_dragged());
  841. event_generator->ReleaseLeftButton();
  842. ASSERT_FALSE(window_state->is_dragged());
  843. // Press pressed outside of the window.
  844. event_generator->MoveMouseTo(gfx::Point(200, 50));
  845. event_generator->PressLeftButton();
  846. shell_surface->StartDrag(HTTOP, gfx::PointF(0, 0));
  847. ASSERT_FALSE(window_state->is_dragged());
  848. }
  849. TEST_F(ClientControlledShellSurfaceTest, ResizabilityAndSizeConstraints) {
  850. auto shell_surface = exo::test::ShellSurfaceBuilder()
  851. .SetMinimumSize(gfx::Size(0, 0))
  852. .SetMaximumSize(gfx::Size(0, 0))
  853. .BuildClientControlledShellSurface();
  854. EXPECT_FALSE(shell_surface->GetWidget()->widget_delegate()->CanResize());
  855. shell_surface->SetMinimumSize(gfx::Size(400, 400));
  856. shell_surface->SetMaximumSize(gfx::Size(0, 0));
  857. auto* surface = shell_surface->root_surface();
  858. surface->Commit();
  859. EXPECT_TRUE(shell_surface->GetWidget()->widget_delegate()->CanResize());
  860. shell_surface->SetMinimumSize(gfx::Size(400, 400));
  861. shell_surface->SetMaximumSize(gfx::Size(400, 400));
  862. surface->Commit();
  863. EXPECT_FALSE(shell_surface->GetWidget()->widget_delegate()->CanResize());
  864. }
  865. namespace {
  866. // This class is only meant to used by CloseWindowWhenDraggingTest.
  867. // When a ClientControlledShellSurface is destroyed, its natvie window will be
  868. // hidden first and at that time its window delegate should have been properly
  869. // reset.
  870. class ShellSurfaceWindowObserver : public aura::WindowObserver {
  871. public:
  872. explicit ShellSurfaceWindowObserver(aura::Window* window)
  873. : window_(window),
  874. has_delegate_(ash::WindowState::Get(window)->HasDelegate()) {
  875. window_->AddObserver(this);
  876. }
  877. ShellSurfaceWindowObserver(const ShellSurfaceWindowObserver&) = delete;
  878. ShellSurfaceWindowObserver& operator=(const ShellSurfaceWindowObserver&) =
  879. delete;
  880. ~ShellSurfaceWindowObserver() override {
  881. if (window_) {
  882. window_->RemoveObserver(this);
  883. window_ = nullptr;
  884. }
  885. }
  886. bool has_delegate() const { return has_delegate_; }
  887. // aura::WindowObserver:
  888. void OnWindowVisibilityChanged(aura::Window* window, bool visible) override {
  889. DCHECK_EQ(window_, window);
  890. if (!visible) {
  891. has_delegate_ = ash::WindowState::Get(window_)->HasDelegate();
  892. window_->RemoveObserver(this);
  893. window_ = nullptr;
  894. }
  895. }
  896. private:
  897. aura::Window* window_;
  898. bool has_delegate_;
  899. };
  900. } // namespace
  901. // Test that when a shell surface is destroyed during its dragging, its window
  902. // delegate should be reset properly.
  903. TEST_F(ClientControlledShellSurfaceTest, CloseWindowWhenDraggingTest) {
  904. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  905. .SetGeometry({0, 0, 256, 256})
  906. .BuildClientControlledShellSurface();
  907. // Press on the edge of the window and start dragging.
  908. gfx::Point touch_location(256, 150);
  909. ui::test::EventGenerator* event_generator = GetEventGenerator();
  910. event_generator->MoveTouch(touch_location);
  911. event_generator->PressTouch();
  912. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  913. EXPECT_TRUE(ash::WindowState::Get(window)->is_dragged());
  914. auto observer = std::make_unique<ShellSurfaceWindowObserver>(window);
  915. EXPECT_TRUE(observer->has_delegate());
  916. // Destroy the window.
  917. shell_surface.reset();
  918. EXPECT_FALSE(observer->has_delegate());
  919. }
  920. namespace {
  921. class ClientControlledShellSurfaceDragTest : public test::ExoTestBase {
  922. public:
  923. ClientControlledShellSurfaceDragTest() = default;
  924. ClientControlledShellSurfaceDragTest(
  925. const ClientControlledShellSurfaceDragTest&) = delete;
  926. ClientControlledShellSurfaceDragTest& operator=(
  927. const ClientControlledShellSurfaceDragTest&) = delete;
  928. ~ClientControlledShellSurfaceDragTest() override = default;
  929. // Sends a gesture scroll sequence to TabletModeAppWindowDragController.
  930. void SendGestureEvents(aura::Window* window,
  931. const gfx::Point& location,
  932. bool fling = false,
  933. float velocity = 0.f) {
  934. ash::WindowState* window_state = ash::WindowState::Get(window);
  935. window_state->CreateDragDetails(gfx::PointF(0, 0), HTCLIENT,
  936. ::wm::WINDOW_MOVE_SOURCE_TOUCH);
  937. std::unique_ptr<ash::TabletModeWindowResizer> controller_ =
  938. std::make_unique<ash::TabletModeWindowResizer>(
  939. window_state,
  940. std::make_unique<ash::TabletModeBrowserWindowDragDelegate>());
  941. controller_->drag_delegate_for_testing()
  942. ->set_drag_start_deadline_for_testing(base::Time::Now());
  943. controller_->Drag(gfx::PointF(location), 0);
  944. if (fling) {
  945. ui::GestureEventDetails details =
  946. ui::GestureEventDetails(ui::ET_SCROLL_FLING_START, 0, velocity);
  947. ui::GestureEvent event =
  948. ui::GestureEvent(location.x(), location.y(), ui::EF_NONE,
  949. base::TimeTicks::Now(), details);
  950. ui::Event::DispatcherApi(&event).set_target(window);
  951. controller_->FlingOrSwipe(&event);
  952. } else {
  953. controller_->CompleteDrag();
  954. }
  955. ash::WindowState::Get(window)->DeleteDragDetails();
  956. }
  957. };
  958. } // namespace
  959. // Test the functionalities of dragging a window from top in tablet mode.
  960. TEST_F(ClientControlledShellSurfaceDragTest, DragWindowFromTopInTabletMode) {
  961. UpdateDisplay("800x600");
  962. ash::Shell* shell = ash::Shell::Get();
  963. shell->tablet_mode_controller()->SetEnabledForTest(true);
  964. auto shell_surface =
  965. exo::test::ShellSurfaceBuilder({800, 552})
  966. .SetGeometry({0, 0, 800, 552})
  967. .SetWindowState(chromeos::WindowStateType::kMaximized)
  968. .BuildClientControlledShellSurface();
  969. auto* surface = shell_surface->root_surface();
  970. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  971. ASSERT_TRUE(ash::WindowState::Get(window)->IsMaximized());
  972. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  973. surface->Commit();
  974. // Drag the window by a small amount of distance will maximize the window
  975. // again.
  976. SendGestureEvents(window, gfx::Point(0, 10));
  977. EXPECT_TRUE(ash::WindowState::Get(window)->IsMaximized());
  978. EXPECT_FALSE(shell->overview_controller()->InOverviewSession());
  979. // FLING the window not inisde preview area with large enough y veloicty
  980. // (larger than kFlingToOverviewThreshold) will drop the window into overview.
  981. SendGestureEvents(
  982. window, gfx::Point(400, 10), /*fling=*/true,
  983. ash::TabletModeWindowDragDelegate::kFlingToOverviewThreshold + 10.f);
  984. ASSERT_TRUE(shell->overview_controller()->InOverviewSession());
  985. EXPECT_TRUE(
  986. shell->overview_controller()->overview_session()->IsWindowInOverview(
  987. window));
  988. // Drag the window long enough (pass one fourth of the screen vertical
  989. // height) to snap the window to splitscreen.
  990. shell->overview_controller()->EndOverview(ash::OverviewEndAction::kTests);
  991. SendGestureEvents(window, gfx::Point(0, 210));
  992. EXPECT_EQ(ash::WindowState::Get(window)->GetStateType(),
  993. WindowStateType::kPrimarySnapped);
  994. }
  995. namespace {
  996. class TestClientControlledShellSurfaceDelegate
  997. : public test::ClientControlledShellSurfaceDelegate {
  998. public:
  999. explicit TestClientControlledShellSurfaceDelegate(
  1000. ClientControlledShellSurface* shell_surface)
  1001. : test::ClientControlledShellSurfaceDelegate(shell_surface) {}
  1002. ~TestClientControlledShellSurfaceDelegate() override = default;
  1003. TestClientControlledShellSurfaceDelegate(
  1004. const TestClientControlledShellSurfaceDelegate&) = delete;
  1005. TestClientControlledShellSurfaceDelegate& operator=(
  1006. const TestClientControlledShellSurfaceDelegate&) = delete;
  1007. int geometry_change_count() const { return geometry_change_count_; }
  1008. std::vector<gfx::Rect> geometry_bounds() const { return geometry_bounds_; }
  1009. int bounds_change_count() const { return bounds_change_count_; }
  1010. std::vector<gfx::Rect> requested_bounds() const { return requested_bounds_; }
  1011. std::vector<int64_t> requested_display_ids() const {
  1012. return requested_display_ids_;
  1013. }
  1014. void Reset() {
  1015. geometry_change_count_ = 0;
  1016. geometry_bounds_.clear();
  1017. bounds_change_count_ = 0;
  1018. requested_bounds_.clear();
  1019. requested_display_ids_.clear();
  1020. }
  1021. static TestClientControlledShellSurfaceDelegate* SetUp(
  1022. ClientControlledShellSurface* shell_surface) {
  1023. return static_cast<TestClientControlledShellSurfaceDelegate*>(
  1024. shell_surface->set_delegate(
  1025. std::make_unique<TestClientControlledShellSurfaceDelegate>(
  1026. shell_surface)));
  1027. }
  1028. private:
  1029. // ClientControlledShellSurface::Delegate:
  1030. void OnGeometryChanged(const gfx::Rect& geometry) override {
  1031. geometry_change_count_++;
  1032. geometry_bounds_.push_back(geometry);
  1033. }
  1034. void OnBoundsChanged(chromeos::WindowStateType current_state,
  1035. chromeos::WindowStateType requested_state,
  1036. int64_t display_id,
  1037. const gfx::Rect& bounds_in_display,
  1038. bool is_resize,
  1039. int bounds_change) override {
  1040. bounds_change_count_++;
  1041. requested_bounds_.push_back(bounds_in_display);
  1042. requested_display_ids_.push_back(display_id);
  1043. }
  1044. int geometry_change_count_ = 0;
  1045. std::vector<gfx::Rect> geometry_bounds_;
  1046. int bounds_change_count_ = 0;
  1047. std::vector<gfx::Rect> requested_bounds_;
  1048. std::vector<int64_t> requested_display_ids_;
  1049. };
  1050. class ClientControlledShellSurfaceDisplayTest : public test::ExoTestBase {
  1051. public:
  1052. ClientControlledShellSurfaceDisplayTest() = default;
  1053. ~ClientControlledShellSurfaceDisplayTest() override = default;
  1054. ClientControlledShellSurfaceDisplayTest(
  1055. const ClientControlledShellSurfaceDisplayTest&) = delete;
  1056. ClientControlledShellSurfaceDisplayTest& operator=(
  1057. const ClientControlledShellSurfaceDisplayTest&) = delete;
  1058. static ash::WindowResizer* CreateDragWindowResizer(
  1059. aura::Window* window,
  1060. const gfx::Point& point_in_parent,
  1061. int window_component) {
  1062. return ash::CreateWindowResizer(window, gfx::PointF(point_in_parent),
  1063. window_component,
  1064. ::wm::WINDOW_MOVE_SOURCE_MOUSE)
  1065. .release();
  1066. }
  1067. gfx::PointF CalculateDragPoint(const ash::WindowResizer& resizer,
  1068. int delta_x,
  1069. int delta_y) {
  1070. gfx::PointF location = resizer.GetInitialLocation();
  1071. location.set_x(location.x() + delta_x);
  1072. location.set_y(location.y() + delta_y);
  1073. return location;
  1074. }
  1075. };
  1076. } // namespace
  1077. TEST_F(ClientControlledShellSurfaceDisplayTest, MoveToAnotherDisplayByDrag) {
  1078. UpdateDisplay("800x600,800x600");
  1079. aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
  1080. auto shell_surface = exo::test::ShellSurfaceBuilder({200, 200})
  1081. .SetNoCommit()
  1082. .BuildClientControlledShellSurface();
  1083. auto* surface = shell_surface->root_surface();
  1084. display::Display primary_display =
  1085. display::Screen::GetScreen()->GetPrimaryDisplay();
  1086. gfx::Rect initial_bounds(-150, 10, 200, 200);
  1087. shell_surface->SetBounds(primary_display.id(), initial_bounds);
  1088. surface->Commit();
  1089. EXPECT_EQ(initial_bounds,
  1090. shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1091. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1092. EXPECT_EQ(root_windows[0], window->GetRootWindow());
  1093. // Prevent snapping |window|. It only distracts from the purpose of the test.
  1094. // TODO: Remove this code after adding functionality where the mouse has to
  1095. // dwell in the snap region before the dragged window can get snapped.
  1096. window->SetProperty(aura::client::kResizeBehaviorKey,
  1097. aura::client::kResizeBehaviorNone);
  1098. ASSERT_FALSE(ash::WindowState::Get(window)->CanSnap());
  1099. std::unique_ptr<ash::WindowResizer> resizer(
  1100. CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
  1101. // Drag the pointer to the right. Once it reaches the right edge of the
  1102. // primary display, it warps to the secondary.
  1103. display::Display secondary_display =
  1104. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]);
  1105. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  1106. // without having to call |CursorManager::SetDisplay|.
  1107. ash::Shell::Get()->cursor_manager()->SetDisplay(secondary_display);
  1108. resizer->Drag(CalculateDragPoint(*resizer, 800, 0), 0);
  1109. auto* delegate =
  1110. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1111. resizer->CompleteDrag();
  1112. EXPECT_EQ(root_windows[1], window->GetRootWindow());
  1113. // TODO(oshima): We currently generate bounds change twice,
  1114. // first when reparented, then set bounds. Chagne wm::SetBoundsInScreen
  1115. // to simply request WM_EVENT_SET_BOUND with target display id.
  1116. ASSERT_EQ(2, delegate->bounds_change_count());
  1117. // Bounds is local to 2nd display.
  1118. EXPECT_EQ(gfx::Rect(-150, 10, 200, 200), delegate->requested_bounds()[0]);
  1119. EXPECT_EQ(gfx::Rect(-150, 10, 200, 200), delegate->requested_bounds()[1]);
  1120. EXPECT_EQ(secondary_display.id(), delegate->requested_display_ids()[0]);
  1121. EXPECT_EQ(secondary_display.id(), delegate->requested_display_ids()[1]);
  1122. }
  1123. TEST_F(ClientControlledShellSurfaceDisplayTest,
  1124. MoveToAnotherDisplayByShortcut) {
  1125. UpdateDisplay("400x600,800x600*2");
  1126. aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
  1127. auto shell_surface = exo::test::ShellSurfaceBuilder({200, 200})
  1128. .BuildClientControlledShellSurface();
  1129. auto* surface = shell_surface->root_surface();
  1130. display::Display primary_display =
  1131. display::Screen::GetScreen()->GetPrimaryDisplay();
  1132. gfx::Rect initial_bounds(-174, 10, 200, 200);
  1133. shell_surface->SetBounds(primary_display.id(), initial_bounds);
  1134. surface->Commit();
  1135. shell_surface->GetWidget()->Show();
  1136. EXPECT_EQ(initial_bounds,
  1137. shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1138. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1139. EXPECT_EQ(root_windows[0], window->GetRootWindow());
  1140. auto* delegate =
  1141. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1142. display::Display secondary_display =
  1143. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]);
  1144. EXPECT_TRUE(
  1145. ash::window_util::MoveWindowToDisplay(window, secondary_display.id()));
  1146. ASSERT_EQ(1, delegate->bounds_change_count());
  1147. // Should be scaled by 2x in pixels on 2x-density density.
  1148. EXPECT_EQ(gfx::Rect(-348, 20, 400, 400), delegate->requested_bounds()[0]);
  1149. EXPECT_EQ(secondary_display.id(), delegate->requested_display_ids()[0]);
  1150. gfx::Rect secondary_position(700, 10, 200, 200);
  1151. shell_surface->SetBounds(secondary_display.id(), secondary_position);
  1152. surface->Commit();
  1153. // Should be scaled by half when converted from pixels to DP.
  1154. EXPECT_EQ(gfx::Rect(750, 5, 100, 100), window->GetBoundsInScreen());
  1155. delegate->Reset();
  1156. // Moving to the outside of another display.
  1157. EXPECT_TRUE(
  1158. ash::window_util::MoveWindowToDisplay(window, primary_display.id()));
  1159. ASSERT_EQ(1, delegate->bounds_change_count());
  1160. // Should fit in the primary display.
  1161. EXPECT_EQ(gfx::Rect(350, 5, 100, 100), delegate->requested_bounds()[0]);
  1162. EXPECT_EQ(primary_display.id(), delegate->requested_display_ids()[0]);
  1163. }
  1164. TEST_F(ClientControlledShellSurfaceTest, CaptionButtonModel) {
  1165. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  1166. .SetGeometry(gfx::Rect(0, 0, 64, 64))
  1167. .BuildClientControlledShellSurface();
  1168. constexpr views::CaptionButtonIcon kAllButtons[] = {
  1169. views::CAPTION_BUTTON_ICON_MINIMIZE,
  1170. views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
  1171. views::CAPTION_BUTTON_ICON_CLOSE,
  1172. views::CAPTION_BUTTON_ICON_BACK,
  1173. views::CAPTION_BUTTON_ICON_MENU,
  1174. views::CAPTION_BUTTON_ICON_FLOAT,
  1175. };
  1176. constexpr uint32_t kAllButtonMask =
  1177. 1 << views::CAPTION_BUTTON_ICON_MINIMIZE |
  1178. 1 << views::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE |
  1179. 1 << views::CAPTION_BUTTON_ICON_CLOSE |
  1180. 1 << views::CAPTION_BUTTON_ICON_BACK |
  1181. 1 << views::CAPTION_BUTTON_ICON_MENU |
  1182. 1 << views::CAPTION_BUTTON_ICON_FLOAT;
  1183. ash::NonClientFrameViewAsh* frame_view =
  1184. static_cast<ash::NonClientFrameViewAsh*>(
  1185. shell_surface->GetWidget()->non_client_view()->frame_view());
  1186. chromeos::FrameCaptionButtonContainerView* container =
  1187. static_cast<ash::HeaderView*>(frame_view->GetHeaderView())
  1188. ->caption_button_container();
  1189. // Visible
  1190. for (auto visible : kAllButtons) {
  1191. uint32_t visible_buttons = 1 << visible;
  1192. shell_surface->SetFrameButtons(visible_buttons, 0);
  1193. const chromeos::CaptionButtonModel* model = container->model();
  1194. for (auto not_visible : kAllButtons) {
  1195. if (not_visible != visible)
  1196. EXPECT_FALSE(model->IsVisible(not_visible));
  1197. }
  1198. EXPECT_TRUE(model->IsVisible(visible));
  1199. EXPECT_FALSE(model->IsEnabled(visible));
  1200. }
  1201. // Enable
  1202. for (auto enabled : kAllButtons) {
  1203. uint32_t enabled_buttons = 1 << enabled;
  1204. shell_surface->SetFrameButtons(kAllButtonMask, enabled_buttons);
  1205. const chromeos::CaptionButtonModel* model = container->model();
  1206. for (auto not_enabled : kAllButtons) {
  1207. if (not_enabled != enabled)
  1208. EXPECT_FALSE(model->IsEnabled(not_enabled));
  1209. }
  1210. EXPECT_TRUE(model->IsEnabled(enabled));
  1211. EXPECT_TRUE(model->IsVisible(enabled));
  1212. }
  1213. // Zoom mode
  1214. EXPECT_FALSE(container->model()->InZoomMode());
  1215. shell_surface->SetFrameButtons(
  1216. kAllButtonMask | 1 << views::CAPTION_BUTTON_ICON_ZOOM, kAllButtonMask);
  1217. EXPECT_TRUE(container->model()->InZoomMode());
  1218. }
  1219. // Makes sure that the "extra title" is respected by the window frame. When not
  1220. // set, there should be no text in the window frame, but the window's name
  1221. // should still be set (for overview mode, accessibility, etc.). When the debug
  1222. // text is set, the window frame should paint it.
  1223. TEST_F(ClientControlledShellSurfaceTest, SetExtraTitle) {
  1224. auto shell_surface = exo::test::ShellSurfaceBuilder({640, 64})
  1225. .BuildClientControlledShellSurface();
  1226. auto* surface = shell_surface->root_surface();
  1227. const std::u16string window_title(u"title");
  1228. shell_surface->SetTitle(window_title);
  1229. const aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1230. EXPECT_EQ(window_title, window->GetTitle());
  1231. EXPECT_FALSE(
  1232. shell_surface->GetWidget()->widget_delegate()->ShouldShowWindowTitle());
  1233. // Paints the frame and returns whether text was drawn. Unforunately the text
  1234. // is a blob so its actual value can't be detected.
  1235. auto paint_does_draw_text = [&shell_surface]() {
  1236. TestCanvas canvas;
  1237. shell_surface->OnSetFrame(SurfaceFrameType::NORMAL);
  1238. ash::NonClientFrameViewAsh* frame_view =
  1239. static_cast<ash::NonClientFrameViewAsh*>(
  1240. shell_surface->GetWidget()->non_client_view()->frame_view());
  1241. frame_view->SetVisible(true);
  1242. // Paint to a layer so we can pass a root PaintInfo.
  1243. frame_view->GetHeaderView()->SetPaintToLayer();
  1244. gfx::Rect bounds(100, 100);
  1245. auto list = base::MakeRefCounted<cc::DisplayItemList>();
  1246. frame_view->GetHeaderView()->Paint(views::PaintInfo::CreateRootPaintInfo(
  1247. ui::PaintContext(list.get(), 1.f, bounds, false), bounds.size()));
  1248. list->Finalize();
  1249. list->Raster(&canvas);
  1250. return canvas.text_was_drawn();
  1251. };
  1252. EXPECT_FALSE(paint_does_draw_text());
  1253. EXPECT_FALSE(
  1254. shell_surface->GetWidget()->widget_delegate()->ShouldShowWindowTitle());
  1255. // Setting the extra title/debug text won't change the window's title, but it
  1256. // will be drawn by the frame header.
  1257. shell_surface->SetExtraTitle(u"extra");
  1258. surface->Commit();
  1259. EXPECT_EQ(window_title, window->GetTitle());
  1260. EXPECT_TRUE(paint_does_draw_text());
  1261. EXPECT_FALSE(
  1262. shell_surface->GetWidget()->widget_delegate()->ShouldShowWindowTitle());
  1263. }
  1264. TEST_F(ClientControlledShellSurfaceTest, WideFrame) {
  1265. auto shell_surface =
  1266. exo::test::ShellSurfaceBuilder({64, 64})
  1267. .SetWindowState(chromeos::WindowStateType::kMaximized)
  1268. .SetGeometry(gfx::Rect(100, 0, 64, 64))
  1269. .SetInputRegion(gfx::Rect(0, 0, 64, 64))
  1270. .SetFrame(SurfaceFrameType::NORMAL)
  1271. .BuildClientControlledShellSurface();
  1272. auto* surface = shell_surface->root_surface();
  1273. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1274. ash::Shelf* shelf = ash::Shelf::ForWindow(window);
  1275. shelf->SetAlignment(ash::ShelfAlignment::kLeft);
  1276. const gfx::Rect work_area =
  1277. display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  1278. const gfx::Rect display_bounds =
  1279. display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
  1280. ASSERT_TRUE(work_area.x() != display_bounds.x());
  1281. auto* wide_frame = shell_surface->wide_frame_for_test();
  1282. ASSERT_TRUE(wide_frame);
  1283. EXPECT_FALSE(wide_frame->header_view()->in_immersive_mode());
  1284. EXPECT_EQ(work_area.x(), wide_frame->GetBoundsInScreen().x());
  1285. EXPECT_EQ(work_area.width(), wide_frame->GetBoundsInScreen().width());
  1286. auto another_window = ash::TestWidgetBuilder().BuildOwnsNativeWidget();
  1287. another_window->SetFullscreen(true);
  1288. // Make sure that the wide frame stays in maximzied size even if there is
  1289. // active fullscreen window.
  1290. EXPECT_EQ(work_area.x(), wide_frame->GetBoundsInScreen().x());
  1291. EXPECT_EQ(work_area.width(), wide_frame->GetBoundsInScreen().width());
  1292. shell_surface->Activate();
  1293. EXPECT_EQ(work_area.x(), wide_frame->GetBoundsInScreen().x());
  1294. EXPECT_EQ(work_area.width(), wide_frame->GetBoundsInScreen().width());
  1295. // If the shelf is set to auto hide by a user, use the display bounds.
  1296. ash::Shelf::ForWindow(window)->SetAutoHideBehavior(
  1297. ash::ShelfAutoHideBehavior::kAlways);
  1298. EXPECT_EQ(display_bounds.x(), wide_frame->GetBoundsInScreen().x());
  1299. EXPECT_EQ(display_bounds.width(), wide_frame->GetBoundsInScreen().width());
  1300. ash::Shelf::ForWindow(window)->SetAutoHideBehavior(
  1301. ash::ShelfAutoHideBehavior::kNever);
  1302. EXPECT_EQ(work_area.x(), wide_frame->GetBoundsInScreen().x());
  1303. EXPECT_EQ(work_area.width(), wide_frame->GetBoundsInScreen().width());
  1304. shell_surface->SetFullscreen(true);
  1305. surface->Commit();
  1306. EXPECT_EQ(display_bounds.x(), wide_frame->GetBoundsInScreen().x());
  1307. EXPECT_EQ(display_bounds.width(), wide_frame->GetBoundsInScreen().width());
  1308. EXPECT_EQ(display_bounds,
  1309. display::Screen::GetScreen()->GetPrimaryDisplay().work_area());
  1310. // Activating maximized window should not affect the fullscreen shell
  1311. // surface's wide frame.
  1312. another_window->Activate();
  1313. another_window->SetFullscreen(false);
  1314. EXPECT_EQ(work_area,
  1315. display::Screen::GetScreen()->GetPrimaryDisplay().work_area());
  1316. EXPECT_EQ(display_bounds.x(), wide_frame->GetBoundsInScreen().x());
  1317. EXPECT_EQ(display_bounds.width(), wide_frame->GetBoundsInScreen().width());
  1318. another_window->Close();
  1319. // Check targeter is still CustomWindowTargeter.
  1320. ASSERT_TRUE(window->parent());
  1321. auto* custom_targeter = window->targeter();
  1322. gfx::Point mouse_location(101, 50);
  1323. auto* root = window->GetRootWindow();
  1324. aura::WindowTargeter targeter;
  1325. aura::Window* target;
  1326. {
  1327. ui::MouseEvent event(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
  1328. ui::EventTimeForNow(), 0, 0);
  1329. target =
  1330. static_cast<aura::Window*>(targeter.FindTargetForEvent(root, &event));
  1331. }
  1332. EXPECT_EQ(surface->window(), target);
  1333. // Disable input region and the targeter no longer find the surface.
  1334. surface->SetInputRegion(gfx::Rect(0, 0, 0, 0));
  1335. surface->Commit();
  1336. {
  1337. ui::MouseEvent event(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
  1338. ui::EventTimeForNow(), 0, 0);
  1339. target =
  1340. static_cast<aura::Window*>(targeter.FindTargetForEvent(root, &event));
  1341. }
  1342. EXPECT_NE(surface->window(), target);
  1343. // Test AUTOHIDE -> NORMAL
  1344. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  1345. surface->Commit();
  1346. EXPECT_TRUE(wide_frame->header_view()->in_immersive_mode());
  1347. surface->SetFrame(SurfaceFrameType::NORMAL);
  1348. surface->Commit();
  1349. EXPECT_FALSE(wide_frame->header_view()->in_immersive_mode());
  1350. EXPECT_EQ(custom_targeter, window->targeter());
  1351. // Test AUTOHIDE -> NONE
  1352. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  1353. surface->Commit();
  1354. EXPECT_TRUE(wide_frame->header_view()->in_immersive_mode());
  1355. // Switching to NONE means no frame so it should delete wide frame.
  1356. surface->SetFrame(SurfaceFrameType::NONE);
  1357. surface->Commit();
  1358. EXPECT_FALSE(shell_surface->wide_frame_for_test());
  1359. {
  1360. ui::MouseEvent event(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
  1361. ui::EventTimeForNow(), 0, 0);
  1362. target =
  1363. static_cast<aura::Window*>(targeter.FindTargetForEvent(root, &event));
  1364. }
  1365. EXPECT_NE(surface->window(), target);
  1366. // Unmaximize it and the frame should be normal.
  1367. shell_surface->SetRestored();
  1368. surface->Commit();
  1369. EXPECT_FALSE(shell_surface->wide_frame_for_test());
  1370. {
  1371. ui::MouseEvent event(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
  1372. ui::EventTimeForNow(), 0, 0);
  1373. target =
  1374. static_cast<aura::Window*>(targeter.FindTargetForEvent(root, &event));
  1375. }
  1376. EXPECT_NE(surface->window(), target);
  1377. // Re-enable input region and the targeter should find the surface again.
  1378. surface->SetInputRegion(gfx::Rect(0, 0, 64, 64));
  1379. surface->Commit();
  1380. {
  1381. ui::MouseEvent event(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
  1382. ui::EventTimeForNow(), 0, 0);
  1383. target =
  1384. static_cast<aura::Window*>(targeter.FindTargetForEvent(root, &event));
  1385. }
  1386. EXPECT_EQ(surface->window(), target);
  1387. }
  1388. // Tests that a WideFrameView is created for an unparented ARC task and that the
  1389. TEST_F(ClientControlledShellSurfaceTest, NoFrameOnModalContainer) {
  1390. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  1391. .SetUseSystemModalContainer()
  1392. .SetGeometry(gfx::Rect(100, 0, 64, 64))
  1393. .SetFrame(SurfaceFrameType::NORMAL)
  1394. .BuildClientControlledShellSurface();
  1395. auto* surface = shell_surface->root_surface();
  1396. EXPECT_FALSE(shell_surface->frame_enabled());
  1397. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  1398. surface->Commit();
  1399. EXPECT_FALSE(shell_surface->frame_enabled());
  1400. }
  1401. TEST_F(ClientControlledShellSurfaceTest,
  1402. SetGeometryReparentsToDisplayOnFirstCommit) {
  1403. UpdateDisplay("100x200,100x200");
  1404. const auto* screen = display::Screen::GetScreen();
  1405. {
  1406. gfx::Rect geometry(16, 16, 32, 32);
  1407. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  1408. .SetGeometry(geometry)
  1409. .BuildClientControlledShellSurface();
  1410. EXPECT_EQ(geometry, shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1411. display::Display primary_display = screen->GetPrimaryDisplay();
  1412. display::Display display = screen->GetDisplayNearestWindow(
  1413. shell_surface->GetWidget()->GetNativeWindow());
  1414. EXPECT_EQ(primary_display.id(), display.id());
  1415. }
  1416. {
  1417. gfx::Rect geometry(116, 16, 32, 32);
  1418. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  1419. .SetGeometry(geometry)
  1420. .BuildClientControlledShellSurface();
  1421. EXPECT_EQ(geometry, shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1422. auto root_windows = ash::Shell::GetAllRootWindows();
  1423. display::Display secondary_display =
  1424. screen->GetDisplayNearestWindow(root_windows[1]);
  1425. display::Display display = screen->GetDisplayNearestWindow(
  1426. shell_surface->GetWidget()->GetNativeWindow());
  1427. EXPECT_EQ(secondary_display.id(), display.id());
  1428. }
  1429. }
  1430. TEST_F(ClientControlledShellSurfaceTest, SetBoundsReparentsToDisplay) {
  1431. UpdateDisplay("100x200,100x200");
  1432. const auto* screen = display::Screen::GetScreen();
  1433. gfx::Rect geometry(16, 16, 32, 32);
  1434. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  1435. .SetGeometry(geometry)
  1436. .BuildClientControlledShellSurface();
  1437. auto* surface = shell_surface->root_surface();
  1438. display::Display primary_display = screen->GetPrimaryDisplay();
  1439. // Move to primary display with bounds inside display.
  1440. shell_surface->SetBounds(primary_display.id(), geometry);
  1441. surface->Commit();
  1442. EXPECT_EQ(geometry, shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1443. display::Display display = screen->GetDisplayNearestWindow(
  1444. shell_surface->GetWidget()->GetNativeWindow());
  1445. EXPECT_EQ(primary_display.id(), display.id());
  1446. auto root_windows = ash::Shell::GetAllRootWindows();
  1447. display::Display secondary_display =
  1448. screen->GetDisplayNearestWindow(root_windows[1]);
  1449. // Move to secondary display with bounds inside display.
  1450. shell_surface->SetBounds(secondary_display.id(), geometry);
  1451. surface->Commit();
  1452. EXPECT_EQ(gfx::Rect(116, 16, 32, 32),
  1453. shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1454. display = screen->GetDisplayNearestWindow(
  1455. shell_surface->GetWidget()->GetNativeWindow());
  1456. EXPECT_EQ(secondary_display.id(), display.id());
  1457. // Move to primary display with bounds outside display.
  1458. geometry.set_origin({-100, 0});
  1459. shell_surface->SetBounds(primary_display.id(), geometry);
  1460. surface->Commit();
  1461. EXPECT_EQ(gfx::Rect(-6, 0, 32, 32),
  1462. shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1463. display = screen->GetDisplayNearestWindow(
  1464. shell_surface->GetWidget()->GetNativeWindow());
  1465. EXPECT_EQ(primary_display.id(), display.id());
  1466. // Move to secondary display with bounds outside display.
  1467. shell_surface->SetBounds(secondary_display.id(), geometry);
  1468. surface->Commit();
  1469. EXPECT_EQ(gfx::Rect(94, 0, 32, 32),
  1470. shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1471. display = screen->GetDisplayNearestWindow(
  1472. shell_surface->GetWidget()->GetNativeWindow());
  1473. EXPECT_EQ(secondary_display.id(), display.id());
  1474. }
  1475. // Test if the surface bounds is correctly set when default scale cancellation
  1476. // is enabled or disabled.
  1477. TEST_F(ClientControlledShellSurfaceTest,
  1478. SetBoundsWithAndWithoutDefaultScaleCancellation) {
  1479. UpdateDisplay("800x600*2,800x600*2");
  1480. const auto primary_display_id =
  1481. display::Screen::GetScreen()->GetPrimaryDisplay().id();
  1482. const auto secondary_display_id =
  1483. display::Screen::GetScreen()->GetAllDisplays().back().id();
  1484. const gfx::Size buffer_size(64, 64);
  1485. std::unique_ptr<Buffer> buffer(
  1486. new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)));
  1487. constexpr double kOriginalScale = 4.f;
  1488. const gfx::Rect bounds_dp(64, 64, 128, 128);
  1489. const gfx::Rect bounds_px_for_2x = gfx::ScaleToRoundedRect(bounds_dp, 2.f);
  1490. const gfx::Rect bounds_px_for_4x =
  1491. gfx::ScaleToRoundedRect(bounds_dp, kOriginalScale);
  1492. for (const auto default_scale_cancellation : {true, false}) {
  1493. SCOPED_TRACE(::testing::Message() << "default_scale_cancellation: "
  1494. << default_scale_cancellation);
  1495. {
  1496. // Set display id, bounds origin, bounds size at the same time via
  1497. // SetBounds method.
  1498. auto builder = exo::test::ShellSurfaceBuilder();
  1499. if (default_scale_cancellation)
  1500. builder.EnableDefaultScaleCancellation();
  1501. auto shell_surface =
  1502. builder.SetNoCommit().BuildClientControlledShellSurface();
  1503. auto* surface = shell_surface->root_surface();
  1504. // When display doesn't change, scale stays the same
  1505. shell_surface->SetScale(kOriginalScale);
  1506. shell_surface->SetDisplay(primary_display_id);
  1507. shell_surface->SetBounds(primary_display_id, default_scale_cancellation
  1508. ? bounds_dp
  1509. : bounds_px_for_4x);
  1510. surface->Attach(buffer.get());
  1511. surface->Commit();
  1512. EXPECT_EQ(bounds_dp,
  1513. shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1514. // When display changes, scale gets updated by the display dsf
  1515. shell_surface->SetScale(kOriginalScale);
  1516. shell_surface->SetBounds(secondary_display_id, default_scale_cancellation
  1517. ? bounds_dp
  1518. : bounds_px_for_2x);
  1519. surface->Attach(buffer.get());
  1520. surface->Commit();
  1521. EXPECT_EQ(bounds_dp.width(),
  1522. shell_surface->GetWidget()->GetWindowBoundsInScreen().width());
  1523. EXPECT_EQ(bounds_dp.height(),
  1524. shell_surface->GetWidget()->GetWindowBoundsInScreen().height());
  1525. }
  1526. {
  1527. // Set display id and bounds origin at the same time via SetBoundsOrigin
  1528. // method, and set bounds size separately.
  1529. const auto bounds_to_set =
  1530. default_scale_cancellation ? bounds_dp : bounds_px_for_4x;
  1531. auto builder = exo::test::ShellSurfaceBuilder();
  1532. if (default_scale_cancellation)
  1533. builder.EnableDefaultScaleCancellation();
  1534. auto shell_surface =
  1535. builder.SetNoCommit().BuildClientControlledShellSurface();
  1536. auto* surface = shell_surface->root_surface();
  1537. shell_surface->SetScale(kOriginalScale);
  1538. shell_surface->SetBoundsOrigin(primary_display_id,
  1539. bounds_to_set.origin());
  1540. shell_surface->SetBoundsSize(bounds_to_set.size());
  1541. surface->Attach(buffer.get());
  1542. surface->Commit();
  1543. EXPECT_EQ(bounds_dp,
  1544. shell_surface->GetWidget()->GetWindowBoundsInScreen());
  1545. }
  1546. }
  1547. }
  1548. // Set orientation lock to a window.
  1549. TEST_F(ClientControlledShellSurfaceTest, SetOrientationLock) {
  1550. display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
  1551. .SetFirstDisplayAsInternalDisplay();
  1552. EnableTabletMode(true);
  1553. ash::ScreenOrientationController* controller =
  1554. ash::Shell::Get()->screen_orientation_controller();
  1555. auto shell_surface =
  1556. exo::test::ShellSurfaceBuilder({256, 256})
  1557. .SetWindowState(chromeos::WindowStateType::kMaximized)
  1558. .BuildClientControlledShellSurface();
  1559. shell_surface->SetOrientationLock(
  1560. chromeos::OrientationType::kLandscapePrimary);
  1561. EXPECT_TRUE(controller->rotation_locked());
  1562. display::Display display(display::Screen::GetScreen()->GetPrimaryDisplay());
  1563. gfx::Size displaySize = display.size();
  1564. EXPECT_GT(displaySize.width(), displaySize.height());
  1565. shell_surface->SetOrientationLock(chromeos::OrientationType::kAny);
  1566. EXPECT_FALSE(controller->rotation_locked());
  1567. EnableTabletMode(false);
  1568. }
  1569. TEST_F(ClientControlledShellSurfaceTest, SetClientAccessibilityId) {
  1570. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  1571. .SetNoCommit()
  1572. .BuildClientControlledShellSurface();
  1573. auto* surface = shell_surface->root_surface();
  1574. EXPECT_FALSE(shell_surface->GetWidget());
  1575. shell_surface->SetClientAccessibilityId(0);
  1576. surface->Commit();
  1577. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1578. EXPECT_EQ(0, *GetShellClientAccessibilityId(window));
  1579. shell_surface->SetClientAccessibilityId(1);
  1580. EXPECT_EQ(1, *GetShellClientAccessibilityId(window));
  1581. shell_surface->SetClientAccessibilityId(-1);
  1582. EXPECT_FALSE(GetShellClientAccessibilityId(window));
  1583. }
  1584. // Tests adjust bounds locally should also request remote client bounds update.
  1585. TEST_F(ClientControlledShellSurfaceTest, AdjustBoundsLocally) {
  1586. UpdateDisplay("800x600");
  1587. gfx::Rect client_bounds(900, 0, 200, 300);
  1588. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  1589. .SetGeometry(client_bounds)
  1590. .SetNoCommit()
  1591. .BuildClientControlledShellSurface();
  1592. auto* delegate =
  1593. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1594. auto* surface = shell_surface->root_surface();
  1595. surface->Commit();
  1596. views::Widget* widget = shell_surface->GetWidget();
  1597. EXPECT_EQ(gfx::Rect(774, 0, 200, 300), widget->GetWindowBoundsInScreen());
  1598. EXPECT_EQ(gfx::Rect(774, 0, 200, 300), delegate->requested_bounds().back());
  1599. // Receiving the same bounds shouldn't try to update the bounds again.
  1600. delegate->Reset();
  1601. shell_surface->SetGeometry(client_bounds);
  1602. surface->Commit();
  1603. EXPECT_EQ(0, delegate->bounds_change_count());
  1604. }
  1605. TEST_F(ClientControlledShellSurfaceTest, SnappedInTabletMode) {
  1606. gfx::Rect client_bounds(256, 256);
  1607. auto shell_surface = exo::test::ShellSurfaceBuilder(client_bounds.size())
  1608. .SetGeometry(client_bounds)
  1609. .BuildClientControlledShellSurface();
  1610. auto* surface = shell_surface->root_surface();
  1611. auto* window = shell_surface->GetWidget()->GetNativeWindow();
  1612. auto* window_state = ash::WindowState::Get(window);
  1613. EnableTabletMode(true);
  1614. ash::WindowSnapWMEvent event(ash::WM_EVENT_SNAP_PRIMARY);
  1615. window_state->OnWMEvent(&event);
  1616. EXPECT_EQ(window_state->GetStateType(), WindowStateType::kPrimarySnapped);
  1617. ash::NonClientFrameViewAsh* frame_view =
  1618. static_cast<ash::NonClientFrameViewAsh*>(
  1619. shell_surface->GetWidget()->non_client_view()->frame_view());
  1620. // Snapped window can also use auto hide.
  1621. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  1622. surface->Commit();
  1623. EXPECT_TRUE(frame_view->GetFrameEnabled());
  1624. EXPECT_TRUE(frame_view->GetHeaderView()->in_immersive_mode());
  1625. }
  1626. TEST_F(ClientControlledShellSurfaceTest, PipWindowCannotBeActivated) {
  1627. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  1628. .BuildClientControlledShellSurface();
  1629. auto* surface = shell_surface->root_surface();
  1630. EXPECT_TRUE(shell_surface->GetWidget()->IsActive());
  1631. EXPECT_TRUE(shell_surface->GetWidget()->CanActivate());
  1632. // Entering PIP should unactivate the window and make the widget
  1633. // unactivatable.
  1634. shell_surface->SetPip();
  1635. surface->Commit();
  1636. EXPECT_FALSE(shell_surface->GetWidget()->IsActive());
  1637. EXPECT_FALSE(shell_surface->GetWidget()->CanActivate());
  1638. // Leaving PIP should make it activatable again.
  1639. shell_surface->SetRestored();
  1640. surface->Commit();
  1641. EXPECT_TRUE(shell_surface->GetWidget()->CanActivate());
  1642. }
  1643. TEST_F(ClientControlledShellSurfaceDisplayTest,
  1644. NoBoundsChangeEventInMinimized) {
  1645. gfx::Rect client_bounds(100, 100);
  1646. auto shell_surface = exo::test::ShellSurfaceBuilder(client_bounds.size())
  1647. .SetGeometry(client_bounds)
  1648. .BuildClientControlledShellSurface();
  1649. auto* surface = shell_surface->root_surface();
  1650. auto* delegate =
  1651. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1652. ASSERT_EQ(0, delegate->bounds_change_count());
  1653. auto* window_state =
  1654. ash::WindowState::Get(shell_surface->GetWidget()->GetNativeWindow());
  1655. int64_t display_id = window_state->GetDisplay().id();
  1656. shell_surface->OnBoundsChangeEvent(WindowStateType::kNormal,
  1657. WindowStateType::kNormal, display_id,
  1658. gfx::Rect(10, 10, 100, 100), 0);
  1659. ASSERT_EQ(1, delegate->bounds_change_count());
  1660. EXPECT_FALSE(shell_surface->GetWidget()->IsMinimized());
  1661. shell_surface->SetMinimized();
  1662. surface->Commit();
  1663. EXPECT_TRUE(shell_surface->GetWidget()->IsMinimized());
  1664. shell_surface->OnBoundsChangeEvent(WindowStateType::kMinimized,
  1665. WindowStateType::kMinimized, display_id,
  1666. gfx::Rect(0, 0, 100, 100), 0);
  1667. ASSERT_EQ(1, delegate->bounds_change_count());
  1668. // Send bounds change when exiting minmized.
  1669. shell_surface->OnBoundsChangeEvent(WindowStateType::kMinimized,
  1670. WindowStateType::kNormal, display_id,
  1671. gfx::Rect(0, 0, 100, 100), 0);
  1672. ASSERT_EQ(2, delegate->bounds_change_count());
  1673. // Snapped, in clamshell mode.
  1674. ash::NonClientFrameViewAsh* frame_view =
  1675. static_cast<ash::NonClientFrameViewAsh*>(
  1676. shell_surface->GetWidget()->non_client_view()->frame_view());
  1677. surface->SetFrame(SurfaceFrameType::NORMAL);
  1678. surface->Commit();
  1679. shell_surface->OnBoundsChangeEvent(WindowStateType::kMinimized,
  1680. WindowStateType::kSecondarySnapped,
  1681. display_id, gfx::Rect(0, 0, 100, 100), 0);
  1682. EXPECT_EQ(3, delegate->bounds_change_count());
  1683. EXPECT_EQ(
  1684. frame_view->GetClientBoundsForWindowBounds(gfx::Rect(0, 0, 100, 100)),
  1685. delegate->requested_bounds().back());
  1686. EXPECT_NE(gfx::Rect(0, 0, 100, 100), delegate->requested_bounds().back());
  1687. // Snapped, in tablet mode.
  1688. EnableTabletMode(true);
  1689. shell_surface->OnBoundsChangeEvent(WindowStateType::kMinimized,
  1690. WindowStateType::kSecondarySnapped,
  1691. display_id, gfx::Rect(0, 0, 100, 100), 0);
  1692. EXPECT_EQ(4, delegate->bounds_change_count());
  1693. EXPECT_EQ(gfx::Rect(0, 0, 100, 100), delegate->requested_bounds().back());
  1694. }
  1695. TEST_F(ClientControlledShellSurfaceTest, SetPipWindowBoundsAnimates) {
  1696. gfx::Rect client_bounds(256, 256);
  1697. auto shell_surface = exo::test::ShellSurfaceBuilder(client_bounds.size())
  1698. .SetWindowState(chromeos::WindowStateType::kPip)
  1699. .SetGeometry(client_bounds)
  1700. .BuildClientControlledShellSurface();
  1701. shell_surface->GetWidget()->Show();
  1702. auto* surface = shell_surface->root_surface();
  1703. // Making an extra commit may set the next bounds change animation type
  1704. // wrongly.
  1705. surface->Commit();
  1706. ui::ScopedAnimationDurationScaleMode animation_scale_mode(
  1707. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  1708. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1709. EXPECT_EQ(gfx::Rect(8, 8, 256, 256), window->layer()->GetTargetBounds());
  1710. EXPECT_EQ(gfx::Rect(8, 8, 256, 256), window->layer()->bounds());
  1711. window->SetBounds(gfx::Rect(10, 10, 256, 256));
  1712. EXPECT_EQ(gfx::Rect(8, 10, 256, 256), window->layer()->GetTargetBounds());
  1713. EXPECT_EQ(gfx::Rect(8, 8, 256, 256), window->layer()->bounds());
  1714. }
  1715. TEST_F(ClientControlledShellSurfaceTest, PipWindowDragDoesNotAnimate) {
  1716. gfx::Rect client_bounds(256, 256);
  1717. auto shell_surface = exo::test::ShellSurfaceBuilder(client_bounds.size())
  1718. .SetWindowState(chromeos::WindowStateType::kPip)
  1719. .SetGeometry(client_bounds)
  1720. .BuildClientControlledShellSurface();
  1721. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1722. EXPECT_EQ(gfx::Rect(8, 8, 256, 256), window->layer()->GetTargetBounds());
  1723. EXPECT_EQ(gfx::Rect(8, 8, 256, 256), window->layer()->bounds());
  1724. ui::ScopedAnimationDurationScaleMode animation_scale_mode(
  1725. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  1726. std::unique_ptr<ash::WindowResizer> resizer(ash::CreateWindowResizer(
  1727. window, gfx::PointF(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_MOUSE));
  1728. resizer->Drag(gfx::PointF(10, 10), 0);
  1729. EXPECT_EQ(gfx::Rect(18, 18, 256, 256), window->layer()->GetTargetBounds());
  1730. EXPECT_EQ(gfx::Rect(18, 18, 256, 256), window->layer()->bounds());
  1731. resizer->CompleteDrag();
  1732. }
  1733. TEST_F(ClientControlledShellSurfaceTest,
  1734. PipWindowDragDoesNotAnimateWithExtraCommit) {
  1735. gfx::Rect client_bounds(256, 256);
  1736. auto shell_surface = exo::test::ShellSurfaceBuilder({client_bounds.size()})
  1737. .SetWindowState(chromeos::WindowStateType::kPip)
  1738. .SetGeometry(client_bounds)
  1739. .BuildClientControlledShellSurface();
  1740. auto* surface = shell_surface->root_surface();
  1741. // Making an extra commit may set the next bounds change animation type
  1742. // wrongly.
  1743. surface->Commit();
  1744. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1745. EXPECT_EQ(gfx::Rect(8, 8, 256, 256), window->layer()->GetTargetBounds());
  1746. EXPECT_EQ(gfx::Rect(8, 8, 256, 256), window->layer()->bounds());
  1747. ui::ScopedAnimationDurationScaleMode animation_scale_mode(
  1748. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  1749. std::unique_ptr<ash::WindowResizer> resizer(ash::CreateWindowResizer(
  1750. window, gfx::PointF(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_MOUSE));
  1751. resizer->Drag(gfx::PointF(10, 10), 0);
  1752. EXPECT_EQ(gfx::Rect(18, 18, 256, 256), window->layer()->GetTargetBounds());
  1753. EXPECT_EQ(gfx::Rect(18, 18, 256, 256), window->layer()->bounds());
  1754. EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
  1755. resizer->CompleteDrag();
  1756. }
  1757. TEST_F(ClientControlledShellSurfaceTest,
  1758. ExpandingPipInTabletModeEndsSplitView) {
  1759. EnableTabletMode(true);
  1760. ash::SplitViewController* split_view_controller =
  1761. ash::SplitViewController::Get(ash::Shell::GetPrimaryRootWindow());
  1762. EXPECT_FALSE(split_view_controller->InSplitViewMode());
  1763. // Create a PIP window:
  1764. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  1765. .SetWindowState(chromeos::WindowStateType::kPip)
  1766. .BuildClientControlledShellSurface();
  1767. auto* surface = shell_surface->root_surface();
  1768. auto window_left = CreateTestWindow();
  1769. auto window_right = CreateTestWindow();
  1770. split_view_controller->SnapWindow(
  1771. window_left.get(), ash::SplitViewController::SnapPosition::LEFT);
  1772. split_view_controller->SnapWindow(
  1773. window_right.get(), ash::SplitViewController::SnapPosition::RIGHT);
  1774. EXPECT_TRUE(split_view_controller->InSplitViewMode());
  1775. // Should end split view.
  1776. shell_surface->SetRestored();
  1777. surface->Commit();
  1778. EXPECT_FALSE(split_view_controller->InSplitViewMode());
  1779. }
  1780. TEST_F(ClientControlledShellSurfaceTest,
  1781. DismissingPipInTabletModeDoesNotEndSplitView) {
  1782. EnableTabletMode(true);
  1783. ash::SplitViewController* split_view_controller =
  1784. ash::SplitViewController::Get(ash::Shell::GetPrimaryRootWindow());
  1785. EXPECT_FALSE(split_view_controller->InSplitViewMode());
  1786. // Create a PIP window:
  1787. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  1788. .SetWindowState(chromeos::WindowStateType::kPip)
  1789. .BuildClientControlledShellSurface();
  1790. auto* surface = shell_surface->root_surface();
  1791. auto window_left = CreateTestWindow();
  1792. auto window_right = CreateTestWindow();
  1793. split_view_controller->SnapWindow(
  1794. window_left.get(), ash::SplitViewController::SnapPosition::LEFT);
  1795. split_view_controller->SnapWindow(
  1796. window_right.get(), ash::SplitViewController::SnapPosition::RIGHT);
  1797. EXPECT_TRUE(split_view_controller->InSplitViewMode());
  1798. // Should not end split-view.
  1799. shell_surface->SetMinimized();
  1800. surface->Commit();
  1801. EXPECT_TRUE(split_view_controller->InSplitViewMode());
  1802. }
  1803. class NoStateChangeDelegate
  1804. : public test::ClientControlledShellSurfaceDelegate {
  1805. public:
  1806. explicit NoStateChangeDelegate(ClientControlledShellSurface* shell_surface)
  1807. : test::ClientControlledShellSurfaceDelegate(shell_surface) {}
  1808. ~NoStateChangeDelegate() override = default;
  1809. NoStateChangeDelegate(const NoStateChangeDelegate&) = delete;
  1810. NoStateChangeDelegate& operator=(const NoStateChangeDelegate&) = delete;
  1811. private:
  1812. // ClientControlledShellSurface::Delegate:
  1813. void OnStateChanged(chromeos::WindowStateType old_state_type,
  1814. chromeos::WindowStateType new_state_type) override {
  1815. EXPECT_TRUE(false);
  1816. }
  1817. };
  1818. TEST_F(ClientControlledShellSurfaceTest, DoNotReplayWindowStateRequest) {
  1819. auto shell_surface =
  1820. exo::test::ShellSurfaceBuilder({64, 64})
  1821. .SetWindowState(chromeos::WindowStateType::kMinimized)
  1822. .SetNoCommit()
  1823. .BuildClientControlledShellSurface();
  1824. auto* surface = shell_surface->root_surface();
  1825. shell_surface->set_delegate(
  1826. std::make_unique<NoStateChangeDelegate>(shell_surface.get()));
  1827. surface->Commit();
  1828. }
  1829. TEST_F(ClientControlledShellSurfaceDisplayTest,
  1830. RequestBoundsChangeOnceWithStateTransition) {
  1831. constexpr gfx::Size buffer_size(64, 64);
  1832. constexpr gfx::Rect original_bounds({20, 20}, buffer_size);
  1833. auto shell_surface = exo::test::ShellSurfaceBuilder(buffer_size)
  1834. .SetWindowState(chromeos::WindowStateType::kNormal)
  1835. .SetGeometry(original_bounds)
  1836. .BuildClientControlledShellSurface();
  1837. auto* surface = shell_surface->root_surface();
  1838. auto* delegate =
  1839. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1840. shell_surface->SetPip();
  1841. surface->Commit();
  1842. ASSERT_EQ(1, delegate->bounds_change_count());
  1843. }
  1844. TEST_F(ClientControlledShellSurfaceTest,
  1845. DoNotSavePipBoundsAcrossMultiplePipTransition) {
  1846. // Create a PIP window:
  1847. constexpr gfx::Size buffer_size(100, 100);
  1848. constexpr gfx::Rect original_bounds({8, 10}, buffer_size);
  1849. auto shell_surface = exo::test::ShellSurfaceBuilder(buffer_size)
  1850. .SetWindowState(chromeos::WindowStateType::kPip)
  1851. .SetGeometry(original_bounds)
  1852. .BuildClientControlledShellSurface();
  1853. auto* surface = shell_surface->root_surface();
  1854. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1855. EXPECT_EQ(gfx::Rect(8, 10, 100, 100), window->bounds());
  1856. const gfx::Rect moved_bounds(gfx::Point(8, 20), buffer_size);
  1857. shell_surface->SetGeometry(moved_bounds);
  1858. surface->Commit();
  1859. EXPECT_EQ(gfx::Rect(8, 20, 100, 100), window->bounds());
  1860. shell_surface->SetRestored();
  1861. surface->Commit();
  1862. shell_surface->SetGeometry(original_bounds);
  1863. shell_surface->SetPip();
  1864. surface->Commit();
  1865. EXPECT_EQ(gfx::Rect(8, 10, 100, 100), window->bounds());
  1866. shell_surface->SetGeometry(moved_bounds);
  1867. surface->Commit();
  1868. EXPECT_EQ(gfx::Rect(8, 20, 100, 100), window->bounds());
  1869. shell_surface->SetRestored();
  1870. surface->Commit();
  1871. shell_surface->SetGeometry(moved_bounds);
  1872. shell_surface->SetPip();
  1873. surface->Commit();
  1874. EXPECT_EQ(gfx::Rect(8, 20, 100, 100), window->bounds());
  1875. }
  1876. TEST_F(ClientControlledShellSurfaceTest,
  1877. DoNotApplyCollisionDetectionWhileDragged) {
  1878. constexpr gfx::Size buffer_size(256, 256);
  1879. constexpr gfx::Rect original_bounds({8, 50}, buffer_size);
  1880. auto shell_surface = exo::test::ShellSurfaceBuilder(buffer_size)
  1881. .SetWindowState(chromeos::WindowStateType::kPip)
  1882. .SetGeometry(original_bounds)
  1883. .BuildClientControlledShellSurface();
  1884. auto* surface = shell_surface->root_surface();
  1885. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1886. ash::WindowState* window_state = ash::WindowState::Get(window);
  1887. EXPECT_EQ(gfx::Rect(8, 50, 256, 256), window->bounds());
  1888. // Ensure that the collision detection logic is not applied during drag move.
  1889. ui::test::EventGenerator* event_generator = GetEventGenerator();
  1890. event_generator->MoveMouseToCenterOf(window);
  1891. event_generator->PressLeftButton();
  1892. shell_surface->StartDrag(HTTOP, gfx::PointF(0, 0));
  1893. ASSERT_TRUE(window_state->is_dragged());
  1894. shell_surface->SetGeometry(gfx::Rect(gfx::Point(20, 50), buffer_size));
  1895. surface->Commit();
  1896. EXPECT_EQ(gfx::Rect(20, 50, 256, 256), window->bounds());
  1897. }
  1898. TEST_F(ClientControlledShellSurfaceTest, EnteringPipSavesPipSnapFraction) {
  1899. constexpr gfx::Size buffer_size(100, 100);
  1900. constexpr gfx::Rect original_bounds({8, 50}, buffer_size);
  1901. auto shell_surface = exo::test::ShellSurfaceBuilder(buffer_size)
  1902. .SetWindowState(chromeos::WindowStateType::kPip)
  1903. .SetGeometry(original_bounds)
  1904. .BuildClientControlledShellSurface();
  1905. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  1906. ash::WindowState* window_state = ash::WindowState::Get(window);
  1907. EXPECT_EQ(gfx::Rect(8, 50, 100, 100), window->bounds());
  1908. // Ensure the correct value is saved to pip snap fraction.
  1909. EXPECT_TRUE(ash::PipPositioner::HasSnapFraction(window_state));
  1910. EXPECT_EQ(ash::PipPositioner::GetSnapFractionAppliedBounds(window_state),
  1911. window->bounds());
  1912. }
  1913. TEST_F(ClientControlledShellSurfaceTest,
  1914. ShadowBoundsChangedIsResetAfterCommit) {
  1915. auto shell_surface =
  1916. exo::test::ShellSurfaceBuilder().BuildClientControlledShellSurface();
  1917. auto* surface = shell_surface->root_surface();
  1918. surface->SetFrame(SurfaceFrameType::SHADOW);
  1919. shell_surface->SetShadowBounds(gfx::Rect(10, 10, 100, 100));
  1920. EXPECT_TRUE(shell_surface->get_shadow_bounds_changed_for_testing());
  1921. surface->Commit();
  1922. EXPECT_FALSE(shell_surface->get_shadow_bounds_changed_for_testing());
  1923. }
  1924. namespace {
  1925. class ClientControlledShellSurfaceScaleTest : public test::ExoTestBase {
  1926. public:
  1927. ClientControlledShellSurfaceScaleTest() = default;
  1928. ~ClientControlledShellSurfaceScaleTest() override = default;
  1929. private:
  1930. ClientControlledShellSurfaceScaleTest(
  1931. const ClientControlledShellSurfaceScaleTest&) = delete;
  1932. ClientControlledShellSurfaceScaleTest& operator=(
  1933. const ClientControlledShellSurfaceScaleTest&) = delete;
  1934. };
  1935. } // namespace
  1936. TEST_F(ClientControlledShellSurfaceScaleTest, ScaleSetOnInitialCommit) {
  1937. UpdateDisplay("1200x800*2.0");
  1938. auto shell_surface = exo::test::ShellSurfaceBuilder({20, 20})
  1939. .SetNoCommit()
  1940. .BuildClientControlledShellSurface();
  1941. auto* surface = shell_surface->root_surface();
  1942. auto* delegate =
  1943. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1944. surface->Commit();
  1945. EXPECT_EQ(2.f, 1.f / shell_surface->GetClientToDpScale());
  1946. EXPECT_EQ(0, delegate->bounds_change_count());
  1947. EXPECT_EQ(1, delegate->geometry_change_count());
  1948. }
  1949. TEST_F(ClientControlledShellSurfaceScaleTest,
  1950. DeferScaleCommitForRestoredWindow) {
  1951. UpdateDisplay("1200x800*2.0");
  1952. gfx::Rect initial_native_bounds(100, 100, 100, 100);
  1953. auto shell_surface = exo::test::ShellSurfaceBuilder({20, 20})
  1954. .SetWindowState(chromeos::WindowStateType::kNormal)
  1955. .SetNoCommit()
  1956. .BuildClientControlledShellSurface();
  1957. auto* delegate =
  1958. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1959. display::Display primary_display =
  1960. display::Screen::GetScreen()->GetPrimaryDisplay();
  1961. shell_surface->SetBounds(primary_display.id(), initial_native_bounds);
  1962. auto* surface = shell_surface->root_surface();
  1963. surface->Commit();
  1964. EXPECT_EQ(2.f, 1.f / shell_surface->GetClientToDpScale());
  1965. EXPECT_EQ(0, delegate->bounds_change_count());
  1966. EXPECT_EQ(1, delegate->geometry_change_count());
  1967. EXPECT_EQ(gfx::ScaleToRoundedRect(initial_native_bounds,
  1968. shell_surface->GetClientToDpScale()),
  1969. delegate->geometry_bounds()[0]);
  1970. UpdateDisplay("2400x1600*1.0");
  1971. // The surface's scale should not be committed until the buffer size changes.
  1972. EXPECT_EQ(2.f, 1.f / shell_surface->GetClientToDpScale());
  1973. EXPECT_EQ(0, delegate->bounds_change_count());
  1974. const gfx::Size new_buffer_size(10, 10);
  1975. std::unique_ptr<Buffer> new_buffer(
  1976. new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(new_buffer_size)));
  1977. surface->Attach(new_buffer.get());
  1978. surface->Commit();
  1979. EXPECT_EQ(1.f, shell_surface->GetClientToDpScale());
  1980. EXPECT_EQ(0, delegate->bounds_change_count());
  1981. }
  1982. TEST_F(ClientControlledShellSurfaceScaleTest,
  1983. CommitScaleChangeImmediatelyForMaximizedWindow) {
  1984. UpdateDisplay("1200x800*2.0");
  1985. gfx::Rect initial_native_bounds(100, 100, 100, 100);
  1986. auto shell_surface =
  1987. exo::test::ShellSurfaceBuilder({20, 20})
  1988. .SetWindowState(chromeos::WindowStateType::kMaximized)
  1989. .SetNoCommit()
  1990. .BuildClientControlledShellSurface();
  1991. auto* surface = shell_surface->root_surface();
  1992. auto* delegate =
  1993. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  1994. display::Display primary_display =
  1995. display::Screen::GetScreen()->GetPrimaryDisplay();
  1996. shell_surface->SetBounds(primary_display.id(), initial_native_bounds);
  1997. surface->Commit();
  1998. EXPECT_EQ(2.f, 1.f / shell_surface->GetClientToDpScale());
  1999. EXPECT_EQ(1, delegate->geometry_change_count());
  2000. EXPECT_EQ(gfx::ScaleToRoundedRect(initial_native_bounds,
  2001. shell_surface->GetClientToDpScale()),
  2002. delegate->geometry_bounds()[0]);
  2003. UpdateDisplay("2400x1600*1.0");
  2004. EXPECT_EQ(1.f, shell_surface->GetClientToDpScale());
  2005. EXPECT_EQ(0, delegate->bounds_change_count());
  2006. }
  2007. TEST_F(ClientControlledShellSurfaceScaleTest,
  2008. CommitScaleChangeImmediatelyInTabletMode) {
  2009. EnableTabletMode(true);
  2010. UpdateDisplay("1200x800*2.0");
  2011. gfx::Rect initial_native_bounds(100, 100, 100, 100);
  2012. auto shell_surface =
  2013. exo::test::ShellSurfaceBuilder({20, 20})
  2014. .SetWindowState(chromeos::WindowStateType::kSecondarySnapped)
  2015. .SetNoCommit()
  2016. .BuildClientControlledShellSurface();
  2017. auto* surface = shell_surface->root_surface();
  2018. auto* delegate =
  2019. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  2020. display::Display primary_display =
  2021. display::Screen::GetScreen()->GetPrimaryDisplay();
  2022. shell_surface->SetBounds(primary_display.id(), initial_native_bounds);
  2023. surface->Commit();
  2024. EXPECT_EQ(2.f, 1.f / shell_surface->GetClientToDpScale());
  2025. EXPECT_EQ(1, delegate->geometry_change_count());
  2026. EXPECT_EQ(gfx::ScaleToRoundedRect(initial_native_bounds,
  2027. shell_surface->GetClientToDpScale()),
  2028. delegate->geometry_bounds()[0]);
  2029. // A bounds change is requested because the window is snapped.
  2030. EXPECT_EQ(1, delegate->bounds_change_count());
  2031. delegate->Reset();
  2032. UpdateDisplay("2400x1600*1.0");
  2033. EXPECT_EQ(1.f, shell_surface->GetClientToDpScale());
  2034. // Updating the scale in tablet mode should request a bounds change, because
  2035. // the window is snapped. Changing the scale will change its bounds in DP,
  2036. // even if the position of the window as visible to the user does not change.
  2037. EXPECT_GE(delegate->bounds_change_count(), 1);
  2038. }
  2039. TEST_F(ClientControlledShellSurfaceTest, SnappedClientBounds) {
  2040. UpdateDisplay("800x600");
  2041. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  2042. .SetNoCommit()
  2043. .BuildClientControlledShellSurface();
  2044. auto* surface = shell_surface->root_surface();
  2045. // Clear insets so that it won't affects the bounds.
  2046. shell_surface->SetSystemUiVisibility(true);
  2047. aura::Window* root = ash::Shell::GetPrimaryRootWindow();
  2048. ash::WorkAreaInsets::ForWindow(root)->UpdateWorkAreaInsetsForTest(
  2049. root, gfx::Rect(), gfx::Insets(), gfx::Insets());
  2050. auto* delegate =
  2051. TestClientControlledShellSurfaceDelegate::SetUp(shell_surface.get());
  2052. surface->Commit();
  2053. views::Widget* widget = shell_surface->GetWidget();
  2054. aura::Window* window = widget->GetNativeWindow();
  2055. // Normal state -> Snap.
  2056. shell_surface->SetGeometry(gfx::Rect(50, 100, 200, 300));
  2057. surface->SetFrame(SurfaceFrameType::NORMAL);
  2058. surface->Commit();
  2059. EXPECT_EQ(gfx::Rect(50, 68, 200, 332), widget->GetWindowBoundsInScreen());
  2060. ash::WindowSnapWMEvent event(ash::WM_EVENT_SNAP_PRIMARY);
  2061. ash::WindowState::Get(window)->OnWMEvent(&event);
  2062. EXPECT_EQ(gfx::Rect(0, 32, 400, 568), delegate->requested_bounds().back());
  2063. // Maximized -> Snap.
  2064. shell_surface->SetMaximized();
  2065. shell_surface->SetGeometry(gfx::Rect(0, 0, 800, 568));
  2066. surface->Commit();
  2067. EXPECT_TRUE(widget->IsMaximized());
  2068. ash::WindowState::Get(window)->OnWMEvent(&event);
  2069. EXPECT_EQ(gfx::Rect(0, 32, 400, 568), delegate->requested_bounds().back());
  2070. shell_surface->SetSnappedToPrimary();
  2071. shell_surface->SetGeometry(gfx::Rect(0, 0, 400, 568));
  2072. surface->Commit();
  2073. // Clamshell mode -> tablet mode. The bounds start from top-left corner.
  2074. EnableTabletMode(true);
  2075. EXPECT_EQ(gfx::Rect(0, 0, 396, 564), delegate->requested_bounds().back());
  2076. shell_surface->SetGeometry(gfx::Rect(0, 0, 396, 568));
  2077. surface->SetFrame(SurfaceFrameType::AUTOHIDE);
  2078. surface->Commit();
  2079. // Tablet mode -> clamshell mode. Top caption height should be reserved.
  2080. EnableTabletMode(false);
  2081. EXPECT_EQ(gfx::Rect(0, 32, 400, 568), delegate->requested_bounds().back());
  2082. // Clean up state.
  2083. shell_surface->SetSnappedToPrimary();
  2084. surface->Commit();
  2085. }
  2086. // The shell surface with resize lock on should be unresizable.
  2087. TEST_F(ClientControlledShellSurfaceTest,
  2088. ShellSurfaceWithResizeLockOnIsUnresizable) {
  2089. auto shell_surface = exo::test::ShellSurfaceBuilder({256, 256})
  2090. .BuildClientControlledShellSurface();
  2091. auto* surface = shell_surface->root_surface();
  2092. EXPECT_TRUE(shell_surface->CanResize());
  2093. shell_surface->SetResizeLockType(
  2094. ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE);
  2095. surface->Commit();
  2096. EXPECT_FALSE(shell_surface->CanResize());
  2097. shell_surface->SetResizeLockType(
  2098. ash::ArcResizeLockType::RESIZE_ENABLED_TOGGLABLE);
  2099. surface->Commit();
  2100. EXPECT_TRUE(shell_surface->CanResize());
  2101. }
  2102. TEST_F(ClientControlledShellSurfaceTest, OverlayShadowBounds) {
  2103. gfx::Rect initial_bounds(150, 10, 200, 200);
  2104. auto shell_surface = exo::test::ShellSurfaceBuilder({1, 1})
  2105. .BuildClientControlledShellSurface();
  2106. auto* surface = shell_surface->root_surface();
  2107. display::Display primary_display =
  2108. display::Screen::GetScreen()->GetPrimaryDisplay();
  2109. shell_surface->SetBounds(primary_display.id(), initial_bounds);
  2110. shell_surface->OnSetFrame(SurfaceFrameType::NORMAL);
  2111. surface->Commit();
  2112. EXPECT_FALSE(shell_surface->HasOverlay());
  2113. ShellSurfaceBase::OverlayParams params(std::make_unique<views::View>());
  2114. params.overlaps_frame = false;
  2115. shell_surface->AddOverlay(std::move(params));
  2116. EXPECT_TRUE(shell_surface->HasOverlay());
  2117. {
  2118. gfx::Size overlay_size =
  2119. shell_surface->GetWidget()->GetWindowBoundsInScreen().size();
  2120. gfx::Size shadow_size = shell_surface->GetShadowBounds().size();
  2121. EXPECT_EQ(shadow_size, overlay_size);
  2122. }
  2123. }
  2124. // WideFrameView follows its respective surface when it is eventually parented.
  2125. // See crbug.com/1223135.
  2126. TEST_F(ClientControlledShellSurfaceTest, WideframeForUnparentedTasks) {
  2127. auto shell_surface = exo::test::ShellSurfaceBuilder({64, 64})
  2128. .SetGeometry(gfx::Rect(100, 0, 64, 64))
  2129. .SetInputRegion(gfx::Rect(0, 0, 64, 64))
  2130. .SetFrame(SurfaceFrameType::NORMAL)
  2131. .BuildClientControlledShellSurface();
  2132. auto* surface = shell_surface->root_surface();
  2133. auto* wide_frame = shell_surface->wide_frame_for_test();
  2134. ASSERT_FALSE(wide_frame);
  2135. // Set the |app_restore::kParentToHiddenContainerKey| for the surface and
  2136. // reparent it, simulating the Full Restore process for an unparented ARC
  2137. // task.
  2138. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  2139. window->SetProperty(app_restore::kParentToHiddenContainerKey, true);
  2140. aura::client::ParentWindowWithContext(window,
  2141. /*context=*/window->GetRootWindow(),
  2142. window->GetBoundsInScreen());
  2143. // Maximize the surface. The WideFrameView should be created and a crash
  2144. // should not occur.
  2145. shell_surface->SetMaximized();
  2146. surface->Commit();
  2147. const auto* hidden_container_parent = window->parent();
  2148. wide_frame = shell_surface->wide_frame_for_test();
  2149. EXPECT_TRUE(wide_frame);
  2150. EXPECT_EQ(hidden_container_parent,
  2151. wide_frame->GetWidget()->GetNativeWindow()->parent());
  2152. // Call the WindowRestoreController, simulating the ARC task becoming ready.
  2153. // The surface should be reparented and the WideFrameView should follow it.
  2154. ash::WindowRestoreController::Get()->OnParentWindowToValidContainer(window);
  2155. EXPECT_NE(hidden_container_parent, window->parent());
  2156. wide_frame = shell_surface->wide_frame_for_test();
  2157. EXPECT_TRUE(wide_frame);
  2158. EXPECT_EQ(window->parent(),
  2159. wide_frame->GetWidget()->GetNativeWindow()->parent());
  2160. }
  2161. TEST_F(ClientControlledShellSurfaceTest,
  2162. InitializeWindowStateGrantsPermissionToActivate) {
  2163. auto shell_surface =
  2164. exo::test::ShellSurfaceBuilder().BuildClientControlledShellSurface();
  2165. aura::Window* window = shell_surface->GetWidget()->GetNativeWindow();
  2166. auto* permission = window->GetProperty(kPermissionKey);
  2167. EXPECT_TRUE(permission->Check(Permission::Capability::kActivate));
  2168. }
  2169. } // namespace exo