Fix FullScreen crash in Webapp
[platform/framework/web/chromium-efl.git] / ash / rounded_display / rounded_display_provider.cc
1 // Copyright 2023 The Chromium Authors
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 "ash/rounded_display/rounded_display_provider.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10 #include <vector>
11
12 #include "ash/display/window_tree_host_manager.h"
13 #include "ash/rounded_display/rounded_display_gutter.h"
14 #include "ash/rounded_display/rounded_display_gutter_factory.h"
15 #include "ash/rounded_display/rounded_display_host.h"
16 #include "ash/screen_util.h"
17 #include "ash/shell.h"
18 #include "base/check.h"
19 #include "base/functional/bind.h"
20 #include "base/notreached.h"
21 #include "ui/display/display.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/geometry/rounded_corners_f.h"
24 #include "ui/gfx/geometry/size.h"
25
26 namespace ash {
27 namespace {
28
29 using Gutters = std::vector<RoundedDisplayGutter*>;
30
31 bool IsRadiiHorizontallyUniform(const gfx::RoundedCornersF& radii) {
32   return radii.upper_left() == radii.upper_right() &&
33          radii.lower_left() == radii.lower_right();
34 }
35
36 bool IsRadiiVerticallyUniform(const gfx::RoundedCornersF& radii) {
37   return radii.upper_left() == radii.lower_left() &&
38          radii.upper_right() == radii.lower_right();
39 }
40
41 bool IsRadiiValid(const gfx::RoundedCornersF radii) {
42   return !radii.IsEmpty() &&
43          (IsRadiiHorizontallyUniform(radii) || IsRadiiVerticallyUniform(radii));
44 }
45
46 // Returns the display's panel size in pixels.
47 gfx::Size GetPanelSizeInPixels(const display::Display& display) {
48   gfx::Size display_size_in_pixels = display.GetSizeInPixel();
49
50   if (display.panel_rotation() == display::Display::ROTATE_90 ||
51       display.panel_rotation() == display::Display::ROTATE_270) {
52     display_size_in_pixels.Transpose();
53   }
54
55   return display_size_in_pixels;
56 }
57
58 const display::Display& GetDisplay(int64_t display_id) {
59   return Shell::Get()->display_manager()->GetDisplayForId(display_id);
60 }
61
62 aura::Window* GetRootWindow(int64_t display_id) {
63   return Shell::GetRootWindowForDisplayId(display_id);
64 }
65
66 }  // namespace
67
68 // static
69 std::unique_ptr<RoundedDisplayProvider> RoundedDisplayProvider::Create(
70     int64_t display_id) {
71   auto gutter_factory = std::make_unique<RoundedDisplayGutterFactory>();
72
73   return std::make_unique<RoundedDisplayProvider>(display_id,
74                                                   std::move(gutter_factory));
75 }
76
77 RoundedDisplayProvider::RoundedDisplayProvider(
78     int64_t display_id,
79     std::unique_ptr<RoundedDisplayGutterFactory> gutter_factory)
80     : display_id_(display_id), gutter_factory_(std::move(gutter_factory)) {}
81
82 RoundedDisplayProvider::~RoundedDisplayProvider() {
83   if (host_) {
84     aura::Window* root_window = GetRootWindow(display_id_);
85     DCHECK(root_window) << "The provider needs to be destroyed first before "
86                            "the root window is destroyed";
87
88     // `host_window_` needs to outlive the `host_`.
89     DCHECK(root_window->Contains(host_window_.get()));
90     root_window->RemoveChild(host_window_.get());
91   }
92 }
93
94 void RoundedDisplayProvider::Init(const gfx::RoundedCornersF& panel_radii,
95                                   Strategy strategy) {
96   if (host_) {
97     NOTREACHED() << "Provider is already initialized";
98   }
99
100   DCHECK(IsRadiiValid(panel_radii));
101
102   current_panel_radii_ = panel_radii;
103   strategy_ = strategy;
104
105   const display::Display& display = GetDisplay(display_id_);
106   CreateGutters(display, panel_radii);
107
108   InitializeHost();
109 }
110
111 void RoundedDisplayProvider::InitializeHost() {
112   host_ = std::make_unique<RoundedDisplayHost>(
113       base::BindRepeating(&RoundedDisplayProvider::GetGuttersInDrawOrder,
114                           weak_ptr_factory_.GetWeakPtr()));
115
116   // TODO(zoraiznaeem): Change the default color to transparent when we fail to
117   // identify surface.
118   host_window_ = std::make_unique<aura::Window>(/*delegate=*/nullptr);
119   host_window_->set_owned_by_parent(false);
120   host_window_->Init(ui::LAYER_SOLID_COLOR);
121   host_window_->SetName("RoundedDisplayHost");
122   host_window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
123   host_window_->SetTransparent(true);
124   host_window_->Show();
125
126   aura::Window* root_window = GetRootWindow(display_id_);
127   root_window->AddChild(host_window_.get());
128
129   host_->Init(host_window_.get());
130 }
131
132 void RoundedDisplayProvider::UpdateHostParent() {
133   DCHECK(host_) << "Call Init() before calling UpdateHostParent";
134
135   aura::Window* new_display_root = GetRootWindow(display_id_);
136   aura::Window* current_display_root = host_window_->GetRootWindow();
137
138   if (new_display_root == current_display_root) {
139     return;
140   }
141
142   current_display_root->RemoveChild(host_window_.get());
143   new_display_root->AddChild(host_window_.get());
144 }
145
146 bool RoundedDisplayProvider::UpdateRoundedDisplaySurface() {
147   DCHECK(host_) << "Call Init() before calling UpdateRoundedDisplay";
148
149   const display::Display& display = GetDisplay(display_id_);
150
151   if (!ShouldSubmitNewCompositorFrame(display)) {
152     return false;
153   }
154
155   // We need to adjust the bounds of host_window to account for the 1px offset
156   // introduced for certain device scale factor values due to conversion between
157   // dip and pixel values.
158   host_window_->SetBounds(screen_util::SnapBoundsToDisplayEdge(
159       gfx::Rect(display.bounds().size()), GetRootWindow(display_id_)));
160
161   gfx::Rect content_rect(host_window_->bounds());
162   gfx::Rect damage_rect;
163
164   // Submit a compositor frame to update the surface. Textures will be reused,
165   // unless we decide to create new gutters, otherwise the positions of the
166   // existing textures will be updated.
167   host_->UpdateSurface(content_rect, damage_rect, /*synchronous_draw=*/true);
168
169   current_device_scale_factor_ = display.device_scale_factor();
170   current_logical_rotation_ = display.rotation();
171
172   return true;
173 }
174
175 bool RoundedDisplayProvider::ShouldSubmitNewCompositorFrame(
176     const display::Display& display) const {
177   return display.device_scale_factor() != current_device_scale_factor_ ||
178          display.rotation() != current_logical_rotation_;
179 }
180
181 void RoundedDisplayProvider::GetGuttersInDrawOrder(Gutters& gutters) const {
182   for (const auto& gutter : overlay_gutters_) {
183     gutters.push_back(gutter.get());
184   }
185 }
186
187 bool RoundedDisplayProvider::CreateGutters(
188     const display::Display& display,
189     const gfx::RoundedCornersF& panel_radii) {
190   gfx::Size panel_size = GetPanelSizeInPixels(display);
191
192   // Scanout direction is left to right wrt to the panel. Therefore horizontal
193   // gutters are in the direction of scanout and vertical gutters are in the
194   // other direction.
195   bool create_vertical_gutters = strategy_ != Strategy::kScanout;
196
197   overlay_gutters_ = gutter_factory_->CreateOverlayGutters(
198       panel_size, panel_radii, create_vertical_gutters);
199
200   return true;
201 }
202
203 }  // namespace ash