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