client_controlled_shell_surface.cc 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/exo/client_controlled_shell_surface.h"
  5. #include <map>
  6. #include <utility>
  7. #include "ash/constants/ash_features.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/arc_resize_lock_type.h"
  12. #include "ash/public/cpp/ash_constants.h"
  13. #include "ash/public/cpp/rounded_corner_utils.h"
  14. #include "ash/public/cpp/shell_window_ids.h"
  15. #include "ash/public/cpp/window_backdrop.h"
  16. #include "ash/public/cpp/window_properties.h"
  17. #include "ash/root_window_controller.h"
  18. #include "ash/screen_util.h"
  19. #include "ash/shell.h"
  20. #include "ash/wm/client_controlled_state.h"
  21. #include "ash/wm/collision_detection/collision_detection_utils.h"
  22. #include "ash/wm/drag_details.h"
  23. #include "ash/wm/pip/pip_positioner.h"
  24. #include "ash/wm/splitview/split_view_controller.h"
  25. #include "ash/wm/toplevel_window_event_handler.h"
  26. #include "ash/wm/window_positioning_utils.h"
  27. #include "ash/wm/window_properties.h"
  28. #include "ash/wm/window_state.h"
  29. #include "ash/wm/window_state_delegate.h"
  30. #include "ash/wm/window_util.h"
  31. #include "base/logging.h"
  32. #include "base/no_destructor.h"
  33. #include "base/numerics/safe_conversions.h"
  34. #include "base/strings/utf_string_conversions.h"
  35. #include "base/trace_event/trace_event.h"
  36. #include "base/trace_event/traced_value.h"
  37. #include "chromeos/ui/base/tablet_state.h"
  38. #include "chromeos/ui/base/window_pin_type.h"
  39. #include "chromeos/ui/base/window_properties.h"
  40. #include "chromeos/ui/base/window_state_type.h"
  41. #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
  42. #include "chromeos/ui/frame/default_frame_header.h"
  43. #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
  44. #include "components/exo/shell_surface_util.h"
  45. #include "components/exo/surface.h"
  46. #include "components/exo/wm_helper.h"
  47. #include "ui/aura/client/aura_constants.h"
  48. #include "ui/aura/scoped_window_event_targeting_blocker.h"
  49. #include "ui/aura/window.h"
  50. #include "ui/aura/window_event_dispatcher.h"
  51. #include "ui/aura/window_observer.h"
  52. #include "ui/aura/window_tree_host.h"
  53. #include "ui/base/class_property.h"
  54. #include "ui/compositor/compositor.h"
  55. #include "ui/compositor/compositor_lock.h"
  56. #include "ui/compositor/layer.h"
  57. #include "ui/display/display.h"
  58. #include "ui/display/screen.h"
  59. #include "ui/display/tablet_state.h"
  60. #include "ui/gfx/geometry/point.h"
  61. #include "ui/gfx/geometry/size.h"
  62. #include "ui/views/widget/widget.h"
  63. #include "ui/wm/core/coordinate_conversion.h"
  64. #include "ui/wm/core/window_util.h"
  65. DEFINE_UI_CLASS_PROPERTY_TYPE(exo::ClientControlledShellSurface*)
  66. namespace exo {
  67. namespace {
  68. using ::ash::screen_util::GetIdealBoundsForMaximizedOrFullscreenOrPinnedState;
  69. using ::chromeos::WindowStateType;
  70. // Client controlled specific accelerators.
  71. const struct {
  72. ui::KeyboardCode keycode;
  73. int modifiers;
  74. ClientControlledAcceleratorAction action;
  75. } kAccelerators[] = {
  76. {ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN,
  77. ClientControlledAcceleratorAction::ZOOM_OUT},
  78. {ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN,
  79. ClientControlledAcceleratorAction::ZOOM_IN},
  80. {ui::VKEY_0, ui::EF_CONTROL_DOWN,
  81. ClientControlledAcceleratorAction::ZOOM_RESET},
  82. };
  83. ClientControlledShellSurface::DelegateFactoryCallback& GetFactoryForTesting() {
  84. using CallbackType = ClientControlledShellSurface::DelegateFactoryCallback;
  85. static base::NoDestructor<CallbackType> factory;
  86. return *factory;
  87. }
  88. // Maximum amount of time to wait for contents that match the display's
  89. // orientation in tablet mode.
  90. // TODO(oshima): Looks like android is generating unnecessary frames.
  91. // Fix it on Android side and reduce the timeout.
  92. constexpr int kOrientationLockTimeoutMs = 2500;
  93. Orientation SizeToOrientation(const gfx::Size& size) {
  94. DCHECK_NE(size.width(), size.height());
  95. return size.width() > size.height() ? Orientation::LANDSCAPE
  96. : Orientation::PORTRAIT;
  97. }
  98. // A ClientControlledStateDelegate that sends the state/bounds
  99. // change request to exo client.
  100. class ClientControlledStateDelegate
  101. : public ash::ClientControlledState::Delegate {
  102. public:
  103. explicit ClientControlledStateDelegate(
  104. ClientControlledShellSurface* shell_surface)
  105. : shell_surface_(shell_surface) {}
  106. ClientControlledStateDelegate(const ClientControlledStateDelegate&) = delete;
  107. ClientControlledStateDelegate& operator=(
  108. const ClientControlledStateDelegate&) = delete;
  109. ~ClientControlledStateDelegate() override {}
  110. // Overridden from ash::ClientControlledState::Delegate:
  111. void HandleWindowStateRequest(ash::WindowState* window_state,
  112. chromeos::WindowStateType next_state) override {
  113. shell_surface_->OnWindowStateChangeEvent(window_state->GetStateType(),
  114. next_state);
  115. }
  116. void HandleBoundsRequest(ash::WindowState* window_state,
  117. chromeos::WindowStateType requested_state,
  118. const gfx::Rect& bounds_in_display,
  119. int64_t display_id) override {
  120. shell_surface_->OnBoundsChangeEvent(
  121. window_state->GetStateType(), requested_state, display_id,
  122. bounds_in_display,
  123. window_state->drag_details() && shell_surface_->IsDragging()
  124. ? window_state->drag_details()->bounds_change
  125. : 0);
  126. }
  127. private:
  128. ClientControlledShellSurface* shell_surface_;
  129. };
  130. // A WindowStateDelegate that implements ToggleFullscreen behavior for
  131. // client controlled window.
  132. class ClientControlledWindowStateDelegate : public ash::WindowStateDelegate {
  133. public:
  134. explicit ClientControlledWindowStateDelegate(
  135. ClientControlledShellSurface* shell_surface,
  136. ash::ClientControlledState::Delegate* delegate)
  137. : shell_surface_(shell_surface), delegate_(delegate) {}
  138. ClientControlledWindowStateDelegate(
  139. const ClientControlledWindowStateDelegate&) = delete;
  140. ClientControlledWindowStateDelegate& operator=(
  141. const ClientControlledWindowStateDelegate&) = delete;
  142. ~ClientControlledWindowStateDelegate() override {}
  143. // Overridden from ash::WindowStateDelegate:
  144. bool ToggleFullscreen(ash::WindowState* window_state) override {
  145. chromeos::WindowStateType next_state;
  146. aura::Window* window = window_state->window();
  147. switch (window_state->GetStateType()) {
  148. case chromeos::WindowStateType::kDefault:
  149. case chromeos::WindowStateType::kNormal:
  150. next_state = chromeos::WindowStateType::kFullscreen;
  151. break;
  152. case chromeos::WindowStateType::kMaximized:
  153. next_state = chromeos::WindowStateType::kFullscreen;
  154. break;
  155. case chromeos::WindowStateType::kFullscreen:
  156. switch (window->GetProperty(aura::client::kRestoreShowStateKey)) {
  157. case ui::SHOW_STATE_DEFAULT:
  158. case ui::SHOW_STATE_NORMAL:
  159. next_state = chromeos::WindowStateType::kNormal;
  160. break;
  161. case ui::SHOW_STATE_MAXIMIZED:
  162. next_state = chromeos::WindowStateType::kMaximized;
  163. break;
  164. case ui::SHOW_STATE_MINIMIZED:
  165. next_state = chromeos::WindowStateType::kMinimized;
  166. break;
  167. case ui::SHOW_STATE_FULLSCREEN:
  168. case ui::SHOW_STATE_INACTIVE:
  169. case ui::SHOW_STATE_END:
  170. NOTREACHED() << " unknown state :"
  171. << window->GetProperty(
  172. aura::client::kRestoreShowStateKey);
  173. return false;
  174. }
  175. break;
  176. case chromeos::WindowStateType::kMinimized: {
  177. next_state = chromeos::WindowStateType::kFullscreen;
  178. break;
  179. }
  180. default:
  181. // TODO(oshima|xdai): Handle SNAP state.
  182. return false;
  183. }
  184. delegate_->HandleWindowStateRequest(window_state, next_state);
  185. return true;
  186. }
  187. void ToggleLockedFullscreen(ash::WindowState*) override {
  188. // No special handling for locked ARC windows.
  189. return;
  190. }
  191. std::unique_ptr<ash::PresentationTimeRecorder> OnDragStarted(
  192. int component) override {
  193. shell_surface_->OnDragStarted(component);
  194. return nullptr;
  195. }
  196. void OnDragFinished(bool canceled, const gfx::PointF& location) override {
  197. shell_surface_->OnDragFinished(canceled, location);
  198. }
  199. private:
  200. ClientControlledShellSurface* shell_surface_;
  201. ash::ClientControlledState::Delegate* delegate_;
  202. };
  203. bool IsPinned(const ash::WindowState* window_state) {
  204. return window_state->IsPinned() || window_state->IsTrustedPinned();
  205. }
  206. class CaptionButtonModel : public chromeos::CaptionButtonModel {
  207. public:
  208. CaptionButtonModel(uint32_t visible_button_mask, uint32_t enabled_button_mask)
  209. : visible_button_mask_(visible_button_mask),
  210. enabled_button_mask_(enabled_button_mask) {}
  211. CaptionButtonModel(const CaptionButtonModel&) = delete;
  212. CaptionButtonModel& operator=(const CaptionButtonModel&) = delete;
  213. // Overridden from ash::CaptionButtonModel:
  214. bool IsVisible(views::CaptionButtonIcon icon) const override {
  215. return visible_button_mask_ & (1 << icon);
  216. }
  217. bool IsEnabled(views::CaptionButtonIcon icon) const override {
  218. return enabled_button_mask_ & (1 << icon);
  219. }
  220. bool InZoomMode() const override {
  221. return visible_button_mask_ & (1 << views::CAPTION_BUTTON_ICON_ZOOM);
  222. }
  223. private:
  224. uint32_t visible_button_mask_;
  225. uint32_t enabled_button_mask_;
  226. };
  227. // EventTargetingBlocker blocks the event targeting by setting NONE targeting
  228. // policy to the window subtrees. It resets to the original policy upon
  229. // deletion.
  230. class EventTargetingBlocker : aura::WindowObserver {
  231. public:
  232. EventTargetingBlocker() = default;
  233. EventTargetingBlocker(const EventTargetingBlocker&) = delete;
  234. EventTargetingBlocker& operator=(const EventTargetingBlocker&) = delete;
  235. ~EventTargetingBlocker() override {
  236. if (window_)
  237. Unregister(window_);
  238. }
  239. void Block(aura::Window* window) {
  240. window_ = window;
  241. Register(window);
  242. }
  243. private:
  244. void Register(aura::Window* window) {
  245. window->AddObserver(this);
  246. event_targeting_blocker_map_[window] =
  247. std::make_unique<aura::ScopedWindowEventTargetingBlocker>(window);
  248. for (auto* child : window->children())
  249. Register(child);
  250. }
  251. void Unregister(aura::Window* window) {
  252. window->RemoveObserver(this);
  253. event_targeting_blocker_map_.erase(window);
  254. for (auto* child : window->children())
  255. Unregister(child);
  256. }
  257. void OnWindowDestroying(aura::Window* window) override {
  258. Unregister(window);
  259. if (window_ == window)
  260. window_ = nullptr;
  261. }
  262. std::map<aura::Window*,
  263. std::unique_ptr<aura::ScopedWindowEventTargetingBlocker>>
  264. event_targeting_blocker_map_;
  265. aura::Window* window_ = nullptr;
  266. };
  267. } // namespace
  268. class ClientControlledShellSurface::ScopedSetBoundsLocally {
  269. public:
  270. explicit ScopedSetBoundsLocally(ClientControlledShellSurface* shell_surface)
  271. : state_(shell_surface->client_controlled_state_) {
  272. state_->set_bounds_locally(true);
  273. }
  274. ScopedSetBoundsLocally(const ScopedSetBoundsLocally&) = delete;
  275. ScopedSetBoundsLocally& operator=(const ScopedSetBoundsLocally&) = delete;
  276. ~ScopedSetBoundsLocally() { state_->set_bounds_locally(false); }
  277. private:
  278. ash::ClientControlledState* const state_;
  279. };
  280. class ClientControlledShellSurface::ScopedLockedToRoot {
  281. public:
  282. explicit ScopedLockedToRoot(views::Widget* widget)
  283. : window_(widget->GetNativeWindow()) {
  284. window_->SetProperty(ash::kLockedToRootKey, true);
  285. }
  286. ScopedLockedToRoot(const ScopedLockedToRoot&) = delete;
  287. ScopedLockedToRoot& operator=(const ScopedLockedToRoot&) = delete;
  288. ~ScopedLockedToRoot() { window_->ClearProperty(ash::kLockedToRootKey); }
  289. private:
  290. aura::Window* const window_;
  291. };
  292. ////////////////////////////////////////////////////////////////////////////////
  293. // ClientControlledShellSurface, public:
  294. ClientControlledShellSurface::ClientControlledShellSurface(
  295. Surface* surface,
  296. bool can_minimize,
  297. int container,
  298. bool default_scale_cancellation)
  299. : ShellSurfaceBase(surface, gfx::Point(), can_minimize, container),
  300. use_default_scale_cancellation_(default_scale_cancellation) {
  301. server_side_resize_ = true;
  302. }
  303. ClientControlledShellSurface::~ClientControlledShellSurface() {
  304. // Reset the window delegate here so that we won't try to do any dragging
  305. // operation on a to-be-destroyed window. |widget_| can be nullptr in tests.
  306. if (GetWidget())
  307. GetWindowState()->SetDelegate(nullptr);
  308. if (client_controlled_state_)
  309. client_controlled_state_->ResetDelegate();
  310. wide_frame_.reset();
  311. }
  312. void ClientControlledShellSurface::SetBounds(int64_t display_id,
  313. const gfx::Rect& bounds) {
  314. TRACE_EVENT2("exo", "ClientControlledShellSurface::SetBounds", "display_id",
  315. display_id, "bounds", bounds.ToString());
  316. if (bounds.IsEmpty()) {
  317. DLOG(WARNING) << "Bounds must be non-empty";
  318. return;
  319. }
  320. // When use_default_scale_cancellation_ is false, the client is scale-aware
  321. // and we expect that the |bounds| has been calculated by the client based
  322. // on the device_scale_factor of the display with |display_id|.
  323. // If the display has been changed before |SetBounds()| is called, for some
  324. // cases(eg. move ARC window between displays with shortcut), |pending_scale_|
  325. // may be stale and tied the old pending_display. Therefore, we re-initialize
  326. // it to 0.0 here to force an update on the value in |EnsurePendingScale()|.
  327. // Also need to note that we only want to commit |pending_scale_| in the cases
  328. // where it hasn't been initialized before this method call.
  329. bool const commit_immediately = pending_scale_ == 0.0;
  330. if (!use_default_scale_cancellation_ && display_id != pending_display_id_)
  331. pending_scale_ = 0.0;
  332. SetDisplay(display_id);
  333. EnsurePendingScale(commit_immediately);
  334. const gfx::Rect bounds_dp =
  335. gfx::ScaleToRoundedRect(bounds, GetClientToDpPendingScale());
  336. SetGeometry(bounds_dp);
  337. }
  338. void ClientControlledShellSurface::SetBoundsOrigin(int64_t display_id,
  339. const gfx::Point& origin) {
  340. TRACE_EVENT2("exo", "ClientControlledShellSurface::SetBoundsOrigin",
  341. "display_id", display_id, "origin", origin.ToString());
  342. SetDisplay(display_id);
  343. EnsurePendingScale(/*commit_immediately=*/true);
  344. const gfx::Point origin_dp =
  345. gfx::ScaleToRoundedPoint(origin, GetClientToDpPendingScale());
  346. pending_geometry_.set_origin(origin_dp);
  347. }
  348. void ClientControlledShellSurface::SetBoundsSize(const gfx::Size& size) {
  349. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetBoundsSize", "size",
  350. size.ToString());
  351. if (size.IsEmpty()) {
  352. DLOG(WARNING) << "Bounds size must be non-empty";
  353. return;
  354. }
  355. EnsurePendingScale(/*commit_immediately=*/true);
  356. const gfx::Size size_dp =
  357. gfx::ScaleToRoundedSize(size, GetClientToDpPendingScale());
  358. pending_geometry_.set_size(size_dp);
  359. }
  360. void ClientControlledShellSurface::SetMaximized() {
  361. TRACE_EVENT0("exo", "ClientControlledShellSurface::SetMaximized");
  362. pending_window_state_ = chromeos::WindowStateType::kMaximized;
  363. }
  364. void ClientControlledShellSurface::SetMinimized() {
  365. TRACE_EVENT0("exo", "ClientControlledShellSurface::SetMinimized");
  366. pending_window_state_ = chromeos::WindowStateType::kMinimized;
  367. }
  368. void ClientControlledShellSurface::SetRestored() {
  369. TRACE_EVENT0("exo", "ClientControlledShellSurface::SetRestored");
  370. pending_window_state_ = chromeos::WindowStateType::kNormal;
  371. }
  372. void ClientControlledShellSurface::SetFullscreen(bool fullscreen) {
  373. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetFullscreen",
  374. "fullscreen", fullscreen);
  375. pending_window_state_ = fullscreen ? chromeos::WindowStateType::kFullscreen
  376. : chromeos::WindowStateType::kNormal;
  377. }
  378. void ClientControlledShellSurface::SetPinned(chromeos::WindowPinType type) {
  379. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetPinned", "type",
  380. static_cast<int>(type));
  381. if (!widget_)
  382. CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
  383. if (type == chromeos::WindowPinType::kNone) {
  384. ash::WindowState::Get(widget_->GetNativeWindow())->Restore();
  385. } else {
  386. bool trusted = type == chromeos::WindowPinType::kTrustedPinned;
  387. ash::window_util::PinWindow(widget_->GetNativeWindow(), trusted);
  388. }
  389. }
  390. void ClientControlledShellSurface::SetSystemUiVisibility(bool autohide) {
  391. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetSystemUiVisibility",
  392. "autohide", autohide);
  393. if (!widget_)
  394. CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
  395. ash::window_util::SetAutoHideShelf(widget_->GetNativeWindow(), autohide);
  396. }
  397. void ClientControlledShellSurface::SetAlwaysOnTop(bool always_on_top) {
  398. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetAlwaysOnTop",
  399. "always_on_top", always_on_top);
  400. pending_always_on_top_ = always_on_top;
  401. }
  402. void ClientControlledShellSurface::SetOrientation(Orientation orientation) {
  403. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetOrientation",
  404. "orientation",
  405. orientation == Orientation::PORTRAIT ? "portrait" : "landscape");
  406. pending_orientation_ = orientation;
  407. }
  408. void ClientControlledShellSurface::SetShadowBounds(const gfx::Rect& bounds) {
  409. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetShadowBounds", "bounds",
  410. bounds.ToString());
  411. auto shadow_bounds =
  412. bounds.IsEmpty() ? absl::nullopt : absl::make_optional(bounds);
  413. if (shadow_bounds_ != shadow_bounds) {
  414. shadow_bounds_ = shadow_bounds;
  415. shadow_bounds_changed_ = true;
  416. }
  417. }
  418. void ClientControlledShellSurface::SetScale(double scale) {
  419. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetScale", "scale", scale);
  420. if (scale <= 0.0) {
  421. DLOG(WARNING) << "Surface scale must be greater than 0";
  422. return;
  423. }
  424. pending_scale_ = scale;
  425. }
  426. void ClientControlledShellSurface::CommitPendingScale() {
  427. if (pending_scale_ == scale_ || pending_scale_ == 0.0)
  428. return;
  429. gfx::Transform transform;
  430. transform.Scale(1.0 / pending_scale_, 1.0 / pending_scale_);
  431. host_window()->SetTransform(transform);
  432. scale_ = pending_scale_;
  433. UpdateCornerRadius();
  434. }
  435. void ClientControlledShellSurface::SetTopInset(int height) {
  436. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetTopInset", "height",
  437. height);
  438. pending_top_inset_height_ = height;
  439. }
  440. void ClientControlledShellSurface::OnWindowStateChangeEvent(
  441. chromeos::WindowStateType current_state,
  442. chromeos::WindowStateType next_state) {
  443. // Android already knows this state change. Don't send state change to Android
  444. // that it is about to do anyway.
  445. if (delegate_ && pending_window_state_ != next_state)
  446. delegate_->OnStateChanged(current_state, next_state);
  447. }
  448. void ClientControlledShellSurface::StartDrag(int component,
  449. const gfx::PointF& location) {
  450. TRACE_EVENT2("exo", "ClientControlledShellSurface::StartDrag", "component",
  451. component, "location", location.ToString());
  452. if (!widget_)
  453. return;
  454. AttemptToStartDrag(component, location);
  455. }
  456. void ClientControlledShellSurface::AttemptToStartDrag(
  457. int component,
  458. const gfx::PointF& location) {
  459. aura::Window* target = widget_->GetNativeWindow();
  460. ash::ToplevelWindowEventHandler* toplevel_handler =
  461. ash::Shell::Get()->toplevel_window_event_handler();
  462. aura::Window* mouse_pressed_handler =
  463. target->GetHost()->dispatcher()->mouse_pressed_handler();
  464. // Start dragging only if ...
  465. // 1) touch guesture is in progres.
  466. // 2) mouse was pressed on the target or its subsurfaces.
  467. if (toplevel_handler->gesture_target() ||
  468. (mouse_pressed_handler && target->Contains(mouse_pressed_handler))) {
  469. gfx::PointF point_in_root(location);
  470. if (use_default_scale_cancellation_) {
  471. // When default scale cancellation is enabled, the client sends the
  472. // location in screen coordinates. Otherwise, the location should already
  473. // be in the display's coordinates.
  474. wm::ConvertPointFromScreen(target->GetRootWindow(), &point_in_root);
  475. }
  476. toplevel_handler->AttemptToStartDrag(
  477. target, point_in_root, component,
  478. ash::ToplevelWindowEventHandler::EndClosure());
  479. }
  480. }
  481. bool ClientControlledShellSurface::IsDragging() {
  482. return in_drag_;
  483. }
  484. void ClientControlledShellSurface::SetCanMaximize(bool can_maximize) {
  485. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetCanMaximize",
  486. "can_maximzie", can_maximize);
  487. can_maximize_ = can_maximize;
  488. if (widget_)
  489. widget_->OnSizeConstraintsChanged();
  490. }
  491. void ClientControlledShellSurface::UpdateAutoHideFrame() {
  492. if (immersive_fullscreen_controller_) {
  493. bool enabled = (frame_type_ == SurfaceFrameType::AUTOHIDE &&
  494. (GetWindowState()->IsMaximizedOrFullscreenOrPinned() ||
  495. GetWindowState()->IsSnapped()));
  496. chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, enabled);
  497. }
  498. }
  499. void ClientControlledShellSurface::SetFrameButtons(
  500. uint32_t visible_button_mask,
  501. uint32_t enabled_button_mask) {
  502. if (frame_visible_button_mask_ == visible_button_mask &&
  503. frame_enabled_button_mask_ == enabled_button_mask) {
  504. return;
  505. }
  506. frame_visible_button_mask_ = visible_button_mask;
  507. frame_enabled_button_mask_ = enabled_button_mask;
  508. if (widget_)
  509. UpdateCaptionButtonModel();
  510. }
  511. void ClientControlledShellSurface::SetExtraTitle(
  512. const std::u16string& extra_title) {
  513. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetExtraTitle",
  514. "extra_title", base::UTF16ToUTF8(extra_title));
  515. if (!widget_) {
  516. initial_extra_title_ = extra_title;
  517. return;
  518. }
  519. GetFrameView()->GetHeaderView()->GetFrameHeader()->SetFrameTextOverride(
  520. extra_title);
  521. if (wide_frame_) {
  522. wide_frame_->header_view()->GetFrameHeader()->SetFrameTextOverride(
  523. extra_title);
  524. }
  525. }
  526. void ClientControlledShellSurface::SetClientAccessibilityId(
  527. int32_t accessibility_id) {
  528. if (accessibility_id >= 0)
  529. client_accessibility_id_ = accessibility_id;
  530. else
  531. client_accessibility_id_.reset();
  532. if (widget_ && widget_->GetNativeWindow()) {
  533. SetShellClientAccessibilityId(widget_->GetNativeWindow(),
  534. client_accessibility_id_);
  535. }
  536. }
  537. void ClientControlledShellSurface::RebindRootSurface(
  538. Surface* root_surface,
  539. bool can_minimize,
  540. int container,
  541. bool default_scale_cancellation) {
  542. use_default_scale_cancellation_ = default_scale_cancellation;
  543. ShellSurfaceBase::RebindRootSurface(root_surface, can_minimize, container);
  544. }
  545. void ClientControlledShellSurface::DidReceiveCompositorFrameAck() {
  546. orientation_ = pending_orientation_;
  547. // Unlock the compositor after the frame is received by viz so that
  548. // screenshot contain the correct frame.
  549. if (expected_orientation_ == orientation_)
  550. orientation_compositor_lock_.reset();
  551. SurfaceTreeHost::DidReceiveCompositorFrameAck();
  552. }
  553. void ClientControlledShellSurface::OnBoundsChangeEvent(
  554. chromeos::WindowStateType current_state,
  555. chromeos::WindowStateType requested_state,
  556. int64_t display_id,
  557. const gfx::Rect& window_bounds,
  558. int bounds_change) {
  559. if (ignore_bounds_change_request_)
  560. return;
  561. // 1) Do no update the bounds unless we have geometry from client.
  562. // 2) Do not update the bounds if window is minimized unless it
  563. // exiting the minimzied state.
  564. // The bounds will be provided by client when unminimized.
  565. if (geometry().IsEmpty() || window_bounds.IsEmpty() ||
  566. (widget_->IsMinimized() &&
  567. requested_state == chromeos::WindowStateType::kMinimized) ||
  568. !delegate_) {
  569. return;
  570. }
  571. // Sends the client bounds, which matches the geometry
  572. // when frame is enabled.
  573. const gfx::Rect client_bounds = GetClientBoundsForWindowBoundsAndWindowState(
  574. window_bounds, requested_state);
  575. gfx::Size current_size = GetFrameView()->GetBoundsForClientView().size();
  576. bool is_resize = client_bounds.size() != current_size &&
  577. !widget_->IsMaximized() && !widget_->IsFullscreen();
  578. // Make sure to use the up-to-date scale factor. At this point, |scale_| or
  579. // |pending_scale_| may not be updated yet.
  580. display::Display display;
  581. const bool display_exists =
  582. display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id,
  583. &display);
  584. DCHECK(display_exists && display.is_valid());
  585. const float scale =
  586. use_default_scale_cancellation_ ? 1.f : display.device_scale_factor();
  587. const gfx::Rect scaled_client_bounds =
  588. gfx::ScaleToRoundedRect(client_bounds, scale);
  589. delegate_->OnBoundsChanged(current_state, requested_state, display_id,
  590. scaled_client_bounds, is_resize, bounds_change);
  591. auto* window_state = GetWindowState();
  592. if (server_reparent_window_ &&
  593. window_state->GetDisplay().id() != display_id) {
  594. ScopedSetBoundsLocally scoped_set_bounds(this);
  595. int container_id = window_state->window()->parent()->GetId();
  596. aura::Window* new_parent =
  597. ash::Shell::GetRootWindowControllerWithDisplayId(display_id)
  598. ->GetContainer(container_id);
  599. new_parent->AddChild(window_state->window());
  600. }
  601. }
  602. void ClientControlledShellSurface::ChangeZoomLevel(ZoomChange change) {
  603. if (delegate_)
  604. delegate_->OnZoomLevelChanged(change);
  605. }
  606. void ClientControlledShellSurface::OnDragStarted(int component) {
  607. in_drag_ = true;
  608. if (delegate_)
  609. delegate_->OnDragStarted(component);
  610. }
  611. void ClientControlledShellSurface::OnDragFinished(bool canceled,
  612. const gfx::PointF& location) {
  613. in_drag_ = false;
  614. if (!delegate_)
  615. return;
  616. const float scale = 1.f / GetClientToDpScale();
  617. const gfx::PointF scaled = gfx::ScalePoint(location, scale);
  618. delegate_->OnDragFinished(scaled.x(), scaled.y(), canceled);
  619. }
  620. float ClientControlledShellSurface::GetClientToDpScale() const {
  621. // If the default_device_scale_factor is used for scale cancellation,
  622. // we expect the client will already send bounds in DP.
  623. if (use_default_scale_cancellation_)
  624. return 1.f;
  625. return 1.f / scale_;
  626. }
  627. void ClientControlledShellSurface::SetResizeLockType(
  628. ash::ArcResizeLockType resize_lock_type) {
  629. TRACE_EVENT1("exo", "ClientControlledShellSurface::SetResizeLockType",
  630. "resize_lock_type", resize_lock_type);
  631. pending_resize_lock_type_ = resize_lock_type;
  632. }
  633. void ClientControlledShellSurface::UpdateResizability() {
  634. TRACE_EVENT0("exo", "ClientControlledShellSurface::updateCanResize");
  635. widget_->GetNativeWindow()->SetProperty(ash::kArcResizeLockTypeKey,
  636. pending_resize_lock_type_);
  637. // If resize lock is enabled, the window is explicitly marded as unresizable.
  638. // Otherwise, the decision is deferred to the parent class.
  639. if (pending_resize_lock_type_ ==
  640. ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE ||
  641. pending_resize_lock_type_ ==
  642. ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE) {
  643. SetCanResize(false);
  644. return;
  645. }
  646. ShellSurfaceBase::UpdateResizability();
  647. }
  648. ////////////////////////////////////////////////////////////////////////////////
  649. // SurfaceDelegate overrides:
  650. bool ClientControlledShellSurface::IsInputEnabled(Surface* surface) const {
  651. // Client-driven dragging/resizing relies on implicit grab, which ensures that
  652. // mouse/touch events are delivered to the focused surface until release, even
  653. // if they fall outside surface bounds. However, if the client destroys the
  654. // surface with implicit grab, the drag/resize is prematurely ended. Prevent
  655. // this by delivering all input events to the root surface, which shares the
  656. // lifetime of the shell surface.
  657. // TODO(domlaskowski): Remove once the client is provided with an API to hook
  658. // into server-driven dragging/resizing.
  659. return surface == root_surface();
  660. }
  661. void ClientControlledShellSurface::OnSetFrame(SurfaceFrameType type) {
  662. pending_frame_type_ = type;
  663. }
  664. void ClientControlledShellSurface::OnSetFrameColors(SkColor active_color,
  665. SkColor inactive_color) {
  666. ShellSurfaceBase::OnSetFrameColors(active_color, inactive_color);
  667. if (wide_frame_) {
  668. aura::Window* window = wide_frame_->GetWidget()->GetNativeWindow();
  669. window->SetProperty(chromeos::kTrackDefaultFrameColors, false);
  670. window->SetProperty(chromeos::kFrameActiveColorKey, active_color);
  671. window->SetProperty(chromeos::kFrameInactiveColorKey, inactive_color);
  672. }
  673. }
  674. void ClientControlledShellSurface::SetSnappedToPrimary() {
  675. TRACE_EVENT0("exo", "ClientControlledShellSurface::SetSnappedToPrimary");
  676. pending_window_state_ = chromeos::WindowStateType::kPrimarySnapped;
  677. }
  678. void ClientControlledShellSurface::SetSnappedToSecondary() {
  679. TRACE_EVENT0("exo", "ClientControlledShellSurface::SetSnappedToSecondary");
  680. pending_window_state_ = chromeos::WindowStateType::kSecondarySnapped;
  681. }
  682. void ClientControlledShellSurface::SetPip() {
  683. TRACE_EVENT0("exo", "ClientControlledShellSurface::SetPip");
  684. pending_window_state_ = chromeos::WindowStateType::kPip;
  685. }
  686. void ClientControlledShellSurface::UnsetPip() {
  687. TRACE_EVENT0("exo", "ClientControlledShellSurface::UnsetPip");
  688. SetRestored();
  689. }
  690. ////////////////////////////////////////////////////////////////////////////////
  691. // aura::WindowObserver overrides:
  692. void ClientControlledShellSurface::OnWindowAddedToRootWindow(
  693. aura::Window* window) {
  694. // Window dragging across display moves the window to target display when
  695. // dropped, but the actual window bounds comes later from android. Update the
  696. // window bounds now so that the window stays where it is expected to be. (it
  697. // may still move if the android sends different bounds).
  698. if (client_controlled_state_->set_bounds_locally() ||
  699. !GetWindowState()->is_dragged()) {
  700. return;
  701. }
  702. ScopedLockedToRoot scoped_locked_to_root(widget_);
  703. UpdateWidgetBounds();
  704. }
  705. ////////////////////////////////////////////////////////////////////////////////
  706. // views::WidgetDelegate overrides:
  707. bool ClientControlledShellSurface::CanMaximize() const {
  708. return can_maximize_;
  709. }
  710. std::unique_ptr<views::NonClientFrameView>
  711. ClientControlledShellSurface::CreateNonClientFrameView(views::Widget* widget) {
  712. ash::WindowState* window_state = GetWindowState();
  713. std::unique_ptr<ash::ClientControlledState::Delegate> delegate =
  714. GetFactoryForTesting()
  715. ? GetFactoryForTesting().Run()
  716. : std::make_unique<ClientControlledStateDelegate>(this);
  717. auto window_delegate = std::make_unique<ClientControlledWindowStateDelegate>(
  718. this, delegate.get());
  719. auto state =
  720. std::make_unique<ash::ClientControlledState>(std::move(delegate));
  721. client_controlled_state_ = state.get();
  722. window_state->SetStateObject(std::move(state));
  723. window_state->SetDelegate(std::move(window_delegate));
  724. auto frame_view = CreateNonClientFrameViewInternal(widget);
  725. immersive_fullscreen_controller_ =
  726. std::make_unique<chromeos::ImmersiveFullscreenController>();
  727. static_cast<ash::NonClientFrameViewAsh*>(frame_view.get())
  728. ->InitImmersiveFullscreenControllerForView(
  729. immersive_fullscreen_controller_.get());
  730. return frame_view;
  731. }
  732. bool ClientControlledShellSurface::ShouldSaveWindowPlacement() const {
  733. return false;
  734. }
  735. void ClientControlledShellSurface::SaveWindowPlacement(
  736. const gfx::Rect& bounds,
  737. ui::WindowShowState show_state) {}
  738. bool ClientControlledShellSurface::GetSavedWindowPlacement(
  739. const views::Widget* widget,
  740. gfx::Rect* bounds,
  741. ui::WindowShowState* show_state) const {
  742. return false;
  743. }
  744. ////////////////////////////////////////////////////////////////////////////////
  745. // views::View overrides:
  746. gfx::Size ClientControlledShellSurface::GetMaximumSize() const {
  747. if (can_maximize_) {
  748. // On ChromeOS, a window with non empty maximum size is non-maximizable,
  749. // even if CanMaximize() returns true. ClientControlledShellSurface
  750. // sololy depends on |can_maximize_| to determine if it is maximizable,
  751. // so just return empty size.
  752. return gfx::Size();
  753. } else {
  754. return ShellSurfaceBase::GetMaximumSize();
  755. }
  756. }
  757. void ClientControlledShellSurface::OnDeviceScaleFactorChanged(float old_dsf,
  758. float new_dsf) {
  759. if (!use_default_scale_cancellation_) {
  760. SetScale(new_dsf);
  761. // Commit scale changes immediately if we expect that the window will not be
  762. // resized.
  763. if (widget_->IsMaximized() || widget_->IsFullscreen() ||
  764. WMHelper::GetInstance()->InTabletMode())
  765. CommitPendingScale();
  766. }
  767. views::View::OnDeviceScaleFactorChanged(old_dsf, new_dsf);
  768. UpdateFrameWidth();
  769. }
  770. ////////////////////////////////////////////////////////////////////////////////
  771. // display::DisplayObserver overrides:
  772. void ClientControlledShellSurface::OnDisplayMetricsChanged(
  773. const display::Display& new_display,
  774. uint32_t changed_metrics) {
  775. SurfaceTreeHost::OnDisplayMetricsChanged(new_display, changed_metrics);
  776. if (!widget_)
  777. return;
  778. // The PIP window bounds is adjusted in Ash when the screen is rotated, but
  779. // Android has an obsolete bounds for a while and applies it incorrectly.
  780. // We need to ignore those bounds change until the states are completely
  781. // synced on both sides.
  782. if (GetWindowState()->IsPip() &&
  783. changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION) {
  784. gfx::Rect bounds_after_rotation =
  785. ash::PipPositioner::GetSnapFractionAppliedBounds(GetWindowState());
  786. display_rotating_with_pip_ =
  787. bounds_after_rotation !=
  788. GetWindowState()->window()->GetBoundsInScreen();
  789. }
  790. const display::Screen* screen = display::Screen::GetScreen();
  791. display::Display current_display =
  792. screen->GetDisplayNearestWindow(widget_->GetNativeWindow());
  793. if (current_display.id() != new_display.id())
  794. return;
  795. bool in_tablet_mode = WMHelper::GetInstance()->InTabletMode();
  796. if (!use_default_scale_cancellation_ &&
  797. changed_metrics &
  798. display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR) {
  799. SetScale(new_display.device_scale_factor());
  800. // Commit scale changes immediately if we expect that the window will not be
  801. // resized.
  802. if (widget_->IsMaximized() || widget_->IsFullscreen() || in_tablet_mode)
  803. CommitPendingScale();
  804. }
  805. if (!in_tablet_mode || !widget_->IsActive() ||
  806. !(changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)) {
  807. return;
  808. }
  809. Orientation target_orientation = SizeToOrientation(new_display.size());
  810. if (orientation_ == target_orientation)
  811. return;
  812. expected_orientation_ = target_orientation;
  813. EnsureCompositorIsLockedForOrientationChange();
  814. }
  815. ////////////////////////////////////////////////////////////////////////////////
  816. // ui::CompositorLockClient overrides:
  817. void ClientControlledShellSurface::CompositorLockTimedOut() {
  818. orientation_compositor_lock_.reset();
  819. }
  820. ////////////////////////////////////////////////////////////////////////////////
  821. // ShellSurfaceBase overrides:
  822. void ClientControlledShellSurface::SetSystemModal(bool system_modal) {
  823. // System modal container is used by clients to implement client side
  824. // managed system modal dialogs using a single ShellSurface instance.
  825. // Hit-test region will be non-empty when at least one dialog exists on
  826. // the client side. Here we detect the transition between no client side
  827. // dialog and at least one dialog so activatable state is properly
  828. // updated.
  829. if (container_ != ash::kShellWindowId_SystemModalContainer) {
  830. LOG(ERROR)
  831. << "Only a window in SystemModalContainer can change the modality";
  832. return;
  833. }
  834. ShellSurfaceBase::SetSystemModal(system_modal);
  835. }
  836. void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds,
  837. bool adjusted_by_server) {
  838. const auto* screen = display::Screen::GetScreen();
  839. aura::Window* window = widget_->GetNativeWindow();
  840. display::Display current_display = screen->GetDisplayNearestWindow(window);
  841. bool is_display_move_pending = false;
  842. display::Display target_display = current_display;
  843. display::Display display;
  844. if (screen->GetDisplayWithDisplayId(display_id_, &display)) {
  845. bool is_display_stale = display_id_ != current_display.id();
  846. // Preserve widget bounds until client acknowledges display move.
  847. if (preserve_widget_bounds_ && is_display_stale)
  848. return;
  849. // True if the window has just been reparented to another root window, and
  850. // the move was initiated by the server.
  851. // TODO(oshima): Improve the window moving logic. https://crbug.com/875047
  852. is_display_move_pending =
  853. window->GetProperty(ash::kLockedToRootKey) && is_display_stale;
  854. if (!is_display_move_pending)
  855. target_display = display;
  856. preserve_widget_bounds_ = is_display_move_pending;
  857. } else {
  858. preserve_widget_bounds_ = false;
  859. }
  860. if (!use_default_scale_cancellation_) {
  861. bool needs_initial_commit = pending_scale_ == 0.0;
  862. SetScale(current_display.device_scale_factor());
  863. if (needs_initial_commit)
  864. CommitPendingScale();
  865. }
  866. // Calculate a minimum window visibility required bounds.
  867. // TODO(oshima): Move this to ComputeAdjustedBounds.
  868. gfx::Rect adjusted_bounds = bounds;
  869. if (!is_display_move_pending) {
  870. const gfx::Rect& restriction = GetWindowState()->IsFullscreen()
  871. ? target_display.bounds()
  872. : target_display.work_area();
  873. ash::ClientControlledState::AdjustBoundsForMinimumWindowVisibility(
  874. restriction, &adjusted_bounds);
  875. // Collision detection to the bounds set by Android should be applied only
  876. // to initial bounds. Do not adjust new bounds as it can be obsolete or in
  877. // transit during animation, which results in incorrect resting postiion.
  878. // The resting position should be fully controlled by chrome afterwards
  879. // because Android isn't aware of Chrome OS System UI.
  880. if (GetWindowState()->IsPip() &&
  881. !ash::PipPositioner::HasSnapFraction(GetWindowState())) {
  882. adjusted_bounds = ash::CollisionDetectionUtils::GetRestingPosition(
  883. target_display, adjusted_bounds,
  884. ash::CollisionDetectionUtils::RelativePriority::kPictureInPicture);
  885. }
  886. }
  887. if (adjusted_bounds == widget_->GetWindowBoundsInScreen() &&
  888. target_display.id() == current_display.id()) {
  889. return;
  890. }
  891. bool set_bounds_locally =
  892. display_rotating_with_pip_ ||
  893. (GetWindowState()->is_dragged() && !is_display_move_pending);
  894. if (set_bounds_locally || client_controlled_state_->set_bounds_locally()) {
  895. // Convert from screen to display coordinates.
  896. gfx::Point origin = bounds.origin();
  897. wm::ConvertPointFromScreen(window->parent(), &origin);
  898. // Move the window relative to the current display.
  899. {
  900. ScopedSetBoundsLocally scoped_set_bounds(this);
  901. window->SetBounds(gfx::Rect(origin, adjusted_bounds.size()));
  902. }
  903. UpdateSurfaceBounds();
  904. return;
  905. }
  906. {
  907. ScopedSetBoundsLocally scoped_set_bounds(this);
  908. window->SetBoundsInScreen(adjusted_bounds, target_display);
  909. }
  910. if (bounds != adjusted_bounds || is_display_move_pending) {
  911. // Notify client that bounds were adjusted or window moved across displays.
  912. auto state_type = GetWindowState()->GetStateType();
  913. gfx::Rect adjusted_bounds_in_display(adjusted_bounds);
  914. adjusted_bounds_in_display.Offset(
  915. -target_display.bounds().OffsetFromOrigin());
  916. OnBoundsChangeEvent(state_type, state_type, target_display.id(),
  917. adjusted_bounds_in_display, 0);
  918. }
  919. UpdateSurfaceBounds();
  920. }
  921. gfx::Rect ClientControlledShellSurface::GetShadowBounds() const {
  922. gfx::Rect shadow_bounds = ShellSurfaceBase::GetShadowBounds();
  923. const ash::NonClientFrameViewAsh* frame_view = GetFrameView();
  924. if (frame_view->GetFrameEnabled() && !shadow_bounds_->IsEmpty() &&
  925. !geometry_.IsEmpty()) {
  926. // The client controlled geometry is only for the client
  927. // area. When the chrome side frame is enabled, the shadow height
  928. // has to include the height of the frame, and the total height is
  929. // equals to the window height computed by
  930. // |GetWindowBoundsForClientBounds|.
  931. shadow_bounds.set_size(
  932. frame_view->GetWindowBoundsForClientBounds(shadow_bounds).size());
  933. }
  934. return shadow_bounds;
  935. }
  936. void ClientControlledShellSurface::InitializeWindowState(
  937. ash::WindowState* window_state) {
  938. // Allow the client to request bounds that do not fill the entire work area
  939. // when maximized, or the entire display when fullscreen.
  940. window_state->set_allow_set_bounds_direct(true);
  941. window_state->set_ignore_keyboard_bounds_change(true);
  942. if (container_ == ash::kShellWindowId_SystemModalContainer ||
  943. container_ == ash::kShellWindowId_ArcVirtualKeyboardContainer) {
  944. DisableMovement();
  945. }
  946. ash::NonClientFrameViewAsh* frame_view = GetFrameView();
  947. frame_view->SetCaptionButtonModel(std::make_unique<CaptionButtonModel>(
  948. frame_visible_button_mask_, frame_enabled_button_mask_));
  949. UpdateAutoHideFrame();
  950. UpdateFrameWidth();
  951. if (initial_orientation_lock_ != chromeos::OrientationType::kAny)
  952. SetOrientationLock(initial_orientation_lock_);
  953. if (initial_extra_title_ != std::u16string())
  954. SetExtraTitle(initial_extra_title_);
  955. // Register Client controlled accelerators.
  956. views::FocusManager* focus_manager = widget_->GetFocusManager();
  957. accelerator_target_ =
  958. std::make_unique<ClientControlledAcceleratorTarget>(this);
  959. // These shortcuts are same as ones used in chrome.
  960. // TODO: investigate if we need to reassign.
  961. for (const auto& entry : kAccelerators) {
  962. focus_manager->RegisterAccelerator(
  963. ui::Accelerator(entry.keycode, entry.modifiers),
  964. ui::AcceleratorManager::kNormalPriority, accelerator_target_.get());
  965. accelerator_target_->RegisterAccelerator(
  966. ui::Accelerator(entry.keycode, entry.modifiers), entry.action);
  967. }
  968. auto* window = widget_->GetNativeWindow();
  969. SetShellClientAccessibilityId(window, client_accessibility_id_);
  970. GrantPermissionToActivateIndefinitely(window);
  971. }
  972. float ClientControlledShellSurface::GetScale() const {
  973. return scale_;
  974. }
  975. absl::optional<gfx::Rect> ClientControlledShellSurface::GetWidgetBounds()
  976. const {
  977. const ash::NonClientFrameViewAsh* frame_view = GetFrameView();
  978. if (frame_view->GetFrameEnabled()) {
  979. gfx::Rect visible_bounds = GetVisibleBounds();
  980. if (widget_->IsMaximized() && frame_type_ == SurfaceFrameType::NORMAL) {
  981. // When the widget is maximized in clamshell mode, client sends
  982. // |geometry_| without taking caption height into account.
  983. visible_bounds.Offset(0, frame_view->NonClientTopBorderHeight());
  984. }
  985. return frame_view->GetWindowBoundsForClientBounds(visible_bounds);
  986. }
  987. return GetVisibleBounds();
  988. }
  989. gfx::Point ClientControlledShellSurface::GetSurfaceOrigin() const {
  990. return gfx::Point();
  991. }
  992. bool ClientControlledShellSurface::OnPreWidgetCommit() {
  993. if (!widget_) {
  994. // Modify the |origin_| to the |pending_geometry_| to place the window on
  995. // the intended display. See b/77472684 for details.
  996. // TODO(domlaskowski): Remove this once clients migrate to geometry API with
  997. // explicit target display.
  998. if (!pending_geometry_.IsEmpty())
  999. origin_ = pending_geometry_.origin();
  1000. CreateShellSurfaceWidget(
  1001. chromeos::ToWindowShowState(pending_window_state_));
  1002. }
  1003. // Finish ignoring obsolete bounds update as the state changes caused by
  1004. // display rotation are synced.
  1005. // TODO(takise): This assumes no other bounds update happens during screen
  1006. // rotation. Implement more robust logic to handle synchronization for
  1007. // screen rotation.
  1008. if (pending_geometry_ != geometry_)
  1009. display_rotating_with_pip_ = false;
  1010. ash::WindowState* window_state = GetWindowState();
  1011. state_changed_ = window_state->GetStateType() != pending_window_state_;
  1012. if (!state_changed_) {
  1013. // Animate PIP window movement unless it is being dragged.
  1014. client_controlled_state_->set_next_bounds_change_animation_type(
  1015. window_state->IsPip() && !window_state->is_dragged()
  1016. ? ash::WindowState::BoundsChangeAnimationType::kAnimate
  1017. : ash::WindowState::BoundsChangeAnimationType::kNone);
  1018. return true;
  1019. }
  1020. if (IsPinned(window_state)) {
  1021. VLOG(1) << "State change was requested while pinned";
  1022. return true;
  1023. }
  1024. auto animation_type = ash::WindowState::BoundsChangeAnimationType::kNone;
  1025. switch (pending_window_state_) {
  1026. case chromeos::WindowStateType::kNormal:
  1027. if (widget_->IsMaximized() || widget_->IsFullscreen()) {
  1028. animation_type =
  1029. ash::WindowState::BoundsChangeAnimationType::kCrossFade;
  1030. }
  1031. break;
  1032. case chromeos::WindowStateType::kMaximized:
  1033. case chromeos::WindowStateType::kFullscreen:
  1034. if (!window_state->IsPip())
  1035. animation_type =
  1036. ash::WindowState::BoundsChangeAnimationType::kCrossFade;
  1037. break;
  1038. default:
  1039. break;
  1040. }
  1041. bool wasPip = window_state->IsPip();
  1042. // As the bounds of the widget is updated later, ensure that no bounds change
  1043. // happens with this state change (e.g. updatePipBounds can be triggered).
  1044. base::AutoReset<bool> resetter(&ignore_bounds_change_request_, true);
  1045. if (client_controlled_state_->EnterNextState(window_state,
  1046. pending_window_state_)) {
  1047. client_controlled_state_->set_next_bounds_change_animation_type(
  1048. animation_type);
  1049. UpdateCornerRadius();
  1050. }
  1051. if (wasPip && !window_state->IsMinimized()) {
  1052. // Expanding PIP should end tablet split view (see crbug.com/941788).
  1053. // Clamshell split view does not require special handling. We activate the
  1054. // PIP window, and so overview ends, which means clamshell split view ends.
  1055. // TODO(edcourtney): Consider not ending tablet split view on PIP expand.
  1056. // See crbug.com/950827.
  1057. ash::SplitViewController* split_view_controller =
  1058. ash::SplitViewController::Get(ash::Shell::GetPrimaryRootWindow());
  1059. if (split_view_controller->InTabletSplitViewMode())
  1060. split_view_controller->EndSplitView();
  1061. // As Android doesn't activate PIP tasks after they are expanded, we need
  1062. // to do it here explicitly.
  1063. // TODO(937738): Investigate if we can activate PIP windows inside commit.
  1064. window_state->Activate();
  1065. }
  1066. return true;
  1067. }
  1068. void ClientControlledShellSurface::OnPostWidgetCommit() {
  1069. DCHECK(widget_);
  1070. UpdateFrame();
  1071. UpdateBackdrop();
  1072. if (delegate_) {
  1073. // Since the visible bounds are in screen coordinates, do not scale these
  1074. // bounds with the display's scale before sending them.
  1075. // TODO(b/167286795): Instead of sending bounds in screen coordinates, send
  1076. // the bounds in the display along with the display information, similar to
  1077. // the bounds_changed_callback_.
  1078. delegate_->OnGeometryChanged(GetVisibleBounds());
  1079. }
  1080. // Apply new top inset height.
  1081. if (pending_top_inset_height_ != top_inset_height_) {
  1082. widget_->GetNativeWindow()->SetProperty(aura::client::kTopViewInset,
  1083. pending_top_inset_height_);
  1084. top_inset_height_ = pending_top_inset_height_;
  1085. }
  1086. // Update surface scale.
  1087. if (use_default_scale_cancellation_)
  1088. CommitPendingScale();
  1089. widget_->GetNativeWindow()->SetProperty(aura::client::kZOrderingKey,
  1090. pending_always_on_top_
  1091. ? ui::ZOrderLevel::kFloatingWindow
  1092. : ui::ZOrderLevel::kNormal);
  1093. UpdateResizability();
  1094. ash::WindowState* window_state = GetWindowState();
  1095. // For PIP, the snap fraction is used to specify the ideal position. Usually
  1096. // this value is set in CompleteDrag, but for the initial position, we need
  1097. // to set it here, when the transition is completed.
  1098. if (window_state->IsPip() &&
  1099. !ash::PipPositioner::HasSnapFraction(window_state)) {
  1100. ash::PipPositioner::SaveSnapFraction(
  1101. window_state, window_state->window()->GetBoundsInScreen());
  1102. }
  1103. ShellSurfaceBase::OnPostWidgetCommit();
  1104. }
  1105. void ClientControlledShellSurface::OnSurfaceDestroying(Surface* surface) {
  1106. if (client_controlled_state_)
  1107. client_controlled_state_->ResetDelegate();
  1108. ShellSurfaceBase::OnSurfaceDestroying(surface);
  1109. }
  1110. void ClientControlledShellSurface::OnContentSizeChanged(Surface* surface) {
  1111. CommitPendingScale();
  1112. }
  1113. ////////////////////////////////////////////////////////////////////////////////
  1114. // ClientControlledShellSurface, private:
  1115. void ClientControlledShellSurface::UpdateFrame() {
  1116. if (!widget_)
  1117. return;
  1118. ash::WindowState* window_state = GetWindowState();
  1119. bool enable_wide_frame = false;
  1120. if (GetFrameView()->GetFrameEnabled() &&
  1121. window_state->IsMaximizedOrFullscreenOrPinned()) {
  1122. gfx::Rect ideal_bounds =
  1123. GetIdealBoundsForMaximizedOrFullscreenOrPinnedState(
  1124. widget_->GetNativeWindow());
  1125. enable_wide_frame = ideal_bounds.width() != geometry().width();
  1126. }
  1127. bool update_frame = state_changed_;
  1128. state_changed_ = false;
  1129. if (enable_wide_frame) {
  1130. if (!wide_frame_) {
  1131. update_frame = true;
  1132. wide_frame_ = std::make_unique<ash::WideFrameView>(widget_);
  1133. chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, false);
  1134. wide_frame_->Init(immersive_fullscreen_controller_.get());
  1135. wide_frame_->header_view()->GetFrameHeader()->SetFrameTextOverride(
  1136. GetFrameView()
  1137. ->GetHeaderView()
  1138. ->GetFrameHeader()
  1139. ->frame_text_override());
  1140. wide_frame_->GetWidget()->Show();
  1141. // Restoring window targeter replaced by ImmersiveFullscreenController.
  1142. InstallCustomWindowTargeter();
  1143. UpdateCaptionButtonModel();
  1144. }
  1145. DCHECK_EQ(chromeos::FrameHeader::Get(widget_),
  1146. wide_frame_->header_view()->GetFrameHeader());
  1147. } else {
  1148. if (wide_frame_) {
  1149. update_frame = true;
  1150. chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, false);
  1151. wide_frame_.reset();
  1152. GetFrameView()->InitImmersiveFullscreenControllerForView(
  1153. immersive_fullscreen_controller_.get());
  1154. // Restoring window targeter replaced by ImmersiveFullscreenController.
  1155. InstallCustomWindowTargeter();
  1156. UpdateCaptionButtonModel();
  1157. }
  1158. DCHECK_EQ(chromeos::FrameHeader::Get(widget_),
  1159. GetFrameView()->GetHeaderView()->GetFrameHeader());
  1160. UpdateFrameWidth();
  1161. }
  1162. // The autohide should be applied when the window state is in
  1163. // maximzied, fullscreen or pinned. Update the auto hide state
  1164. // inside commit.
  1165. if (update_frame)
  1166. UpdateAutoHideFrame();
  1167. }
  1168. void ClientControlledShellSurface::UpdateCaptionButtonModel() {
  1169. auto model = std::make_unique<CaptionButtonModel>(frame_visible_button_mask_,
  1170. frame_enabled_button_mask_);
  1171. if (wide_frame_)
  1172. wide_frame_->SetCaptionButtonModel(std::move(model));
  1173. else
  1174. GetFrameView()->SetCaptionButtonModel(std::move(model));
  1175. }
  1176. void ClientControlledShellSurface::UpdateBackdrop() {
  1177. aura::Window* window = widget_->GetNativeWindow();
  1178. // Always create a backdrop regardless of the geometry because
  1179. // maximized/fullscreen widget's geometry can be cropped.
  1180. bool enable_backdrop = widget_->IsFullscreen() || widget_->IsMaximized();
  1181. ash::WindowBackdrop::BackdropMode target_backdrop_mode =
  1182. enable_backdrop ? ash::WindowBackdrop::BackdropMode::kEnabled
  1183. : ash::WindowBackdrop::BackdropMode::kAuto;
  1184. ash::WindowBackdrop* window_backdrop = ash::WindowBackdrop::Get(window);
  1185. if (window_backdrop->mode() != target_backdrop_mode)
  1186. window_backdrop->SetBackdropMode(target_backdrop_mode);
  1187. }
  1188. void ClientControlledShellSurface::UpdateFrameWidth() {
  1189. int width = -1;
  1190. if (shadow_bounds_) {
  1191. float device_scale_factor =
  1192. GetWidget()->GetNativeWindow()->layer()->device_scale_factor();
  1193. float dsf_to_default_dsf = device_scale_factor / scale_;
  1194. width = base::ClampRound(shadow_bounds_->width() * dsf_to_default_dsf);
  1195. }
  1196. static_cast<ash::HeaderView*>(GetFrameView()->GetHeaderView())
  1197. ->SetWidthInPixels(width);
  1198. }
  1199. void ClientControlledShellSurface::UpdateFrameType() {
  1200. if (container_ == ash::kShellWindowId_SystemModalContainer &&
  1201. pending_frame_type_ != SurfaceFrameType::NONE) {
  1202. LOG(WARNING)
  1203. << "A surface in system modal container should not have a frame:"
  1204. << static_cast<int>(pending_frame_type_);
  1205. return;
  1206. }
  1207. // TODO(oshima): We shouldn't send the synthesized motion event when just
  1208. // changing the frame type. The better solution would be to keep the window
  1209. // position regardless of the frame state, but that won't be available until
  1210. // next arc version.
  1211. // This is a stopgap solution not to generate the event until it is resolved.
  1212. EventTargetingBlocker blocker;
  1213. bool suppress_mouse_event = frame_type_ != pending_frame_type_ && widget_;
  1214. if (suppress_mouse_event)
  1215. blocker.Block(widget_->GetNativeWindow());
  1216. ShellSurfaceBase::OnSetFrame(pending_frame_type_);
  1217. UpdateAutoHideFrame();
  1218. if (suppress_mouse_event)
  1219. UpdateSurfaceBounds();
  1220. }
  1221. bool ClientControlledShellSurface::GetCanResizeFromSizeConstraints() const {
  1222. // Both min and max bounds of unresizable, maximized ARC windows are empty
  1223. // because Ash requires maximizable apps have empty max bounds.
  1224. // This assumes that ARC sets non-empty min sizes to all resizable apps.
  1225. //
  1226. // Example values of size constraints:
  1227. // ----------------------------------------------------------------------
  1228. // | | resizable | non-resizable |
  1229. // ----------------------------------------------------------------------
  1230. // | freeform | min: (400, 400), max: (0, 0) | min = max = window size |
  1231. // ----------------------------------------------------------------------
  1232. // | maximized | min: (400, 400), max: (0, 0) | min = max = (0, 0) |
  1233. // ----------------------------------------------------------------------
  1234. return minimum_size_ != maximum_size_;
  1235. }
  1236. void ClientControlledShellSurface::
  1237. EnsureCompositorIsLockedForOrientationChange() {
  1238. if (!orientation_compositor_lock_) {
  1239. ui::Compositor* compositor =
  1240. widget_->GetNativeWindow()->layer()->GetCompositor();
  1241. orientation_compositor_lock_ = compositor->GetCompositorLock(
  1242. this, base::Milliseconds(kOrientationLockTimeoutMs));
  1243. }
  1244. }
  1245. ash::WindowState* ClientControlledShellSurface::GetWindowState() {
  1246. return ash::WindowState::Get(widget_->GetNativeWindow());
  1247. }
  1248. ash::NonClientFrameViewAsh* ClientControlledShellSurface::GetFrameView() {
  1249. return static_cast<ash::NonClientFrameViewAsh*>(
  1250. widget_->non_client_view()->frame_view());
  1251. }
  1252. const ash::NonClientFrameViewAsh* ClientControlledShellSurface::GetFrameView()
  1253. const {
  1254. return static_cast<const ash::NonClientFrameViewAsh*>(
  1255. widget_->non_client_view()->frame_view());
  1256. }
  1257. void ClientControlledShellSurface::EnsurePendingScale(bool commit_immediately) {
  1258. // Handle the case where we receive bounds from the client before the initial
  1259. // scale has been set or |pending_scale_| is stale due to change of displays.
  1260. if (pending_scale_ == 0.0) {
  1261. DCHECK(!use_default_scale_cancellation_);
  1262. display::Display display;
  1263. if (display::Screen::GetScreen()->GetDisplayWithDisplayId(
  1264. pending_display_id_, &display)) {
  1265. SetScale(display.device_scale_factor());
  1266. if (commit_immediately)
  1267. CommitPendingScale();
  1268. }
  1269. }
  1270. }
  1271. float ClientControlledShellSurface::GetClientToDpPendingScale() const {
  1272. // When the client is scale-aware, we expect that it will resize windows when
  1273. // reacting to scale changes. Since we do not commit the scale until the
  1274. // buffer size changes, any bounds sent after a scale change and before the
  1275. // scale commit will result in mismatched sizes between widget and the buffer.
  1276. // To work around this, we use pending_scale_ to calculate bounds in DP
  1277. // instead of GetClientToDpScale().
  1278. return use_default_scale_cancellation_ ? 1.f : 1.f / pending_scale_;
  1279. }
  1280. gfx::Rect
  1281. ClientControlledShellSurface::GetClientBoundsForWindowBoundsAndWindowState(
  1282. const gfx::Rect& window_bounds,
  1283. chromeos::WindowStateType window_state) const {
  1284. // The client's geometry uses fullscreen in client controlled,
  1285. // (but the surface is placed under the frame), so just use
  1286. // the window bounds instead for maximixed state.
  1287. // Snapped window states in tablet mode do not include the caption height.
  1288. const bool is_snapped =
  1289. window_state == chromeos::WindowStateType::kPrimarySnapped ||
  1290. window_state == chromeos::WindowStateType::kSecondarySnapped;
  1291. const bool is_maximized =
  1292. window_state == chromeos::WindowStateType::kMaximized;
  1293. const display::TabletState tablet_state =
  1294. chromeos::TabletState::Get()->state();
  1295. const bool is_tablet_mode = WMHelper::GetInstance()->InTabletMode();
  1296. if (is_maximized || (is_snapped && is_tablet_mode))
  1297. return window_bounds;
  1298. gfx::Rect client_bounds =
  1299. GetFrameView()->GetClientBoundsForWindowBounds(window_bounds);
  1300. if (is_snapped && tablet_state == display::TabletState::kExitingTabletMode) {
  1301. // Until the next commit, the frame view is in immersive mode, and the above
  1302. // GetClientBoundsForWindowBounds doesn't return bounds taking the caption
  1303. // height into account.
  1304. client_bounds.Inset(gfx::Insets().set_top(
  1305. GetFrameView()->NonClientTopBorderPreferredHeight()));
  1306. }
  1307. return client_bounds;
  1308. }
  1309. // static
  1310. void ClientControlledShellSurface::
  1311. SetClientControlledStateDelegateFactoryForTest(
  1312. const DelegateFactoryCallback& callback) {
  1313. auto& factory = GetFactoryForTesting();
  1314. factory = callback;
  1315. }
  1316. } // namespace exo