12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/exo/client_controlled_shell_surface.h"
- #include <map>
- #include <utility>
- #include "ash/constants/ash_features.h"
- #include "ash/frame/header_view.h"
- #include "ash/frame/non_client_frame_view_ash.h"
- #include "ash/frame/wide_frame_view.h"
- #include "ash/public/cpp/arc_resize_lock_type.h"
- #include "ash/public/cpp/ash_constants.h"
- #include "ash/public/cpp/rounded_corner_utils.h"
- #include "ash/public/cpp/shell_window_ids.h"
- #include "ash/public/cpp/window_backdrop.h"
- #include "ash/public/cpp/window_properties.h"
- #include "ash/root_window_controller.h"
- #include "ash/screen_util.h"
- #include "ash/shell.h"
- #include "ash/wm/client_controlled_state.h"
- #include "ash/wm/collision_detection/collision_detection_utils.h"
- #include "ash/wm/drag_details.h"
- #include "ash/wm/pip/pip_positioner.h"
- #include "ash/wm/splitview/split_view_controller.h"
- #include "ash/wm/toplevel_window_event_handler.h"
- #include "ash/wm/window_positioning_utils.h"
- #include "ash/wm/window_properties.h"
- #include "ash/wm/window_state.h"
- #include "ash/wm/window_state_delegate.h"
- #include "ash/wm/window_util.h"
- #include "base/logging.h"
- #include "base/no_destructor.h"
- #include "base/numerics/safe_conversions.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/trace_event/trace_event.h"
- #include "base/trace_event/traced_value.h"
- #include "chromeos/ui/base/tablet_state.h"
- #include "chromeos/ui/base/window_pin_type.h"
- #include "chromeos/ui/base/window_properties.h"
- #include "chromeos/ui/base/window_state_type.h"
- #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
- #include "chromeos/ui/frame/default_frame_header.h"
- #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
- #include "components/exo/shell_surface_util.h"
- #include "components/exo/surface.h"
- #include "components/exo/wm_helper.h"
- #include "ui/aura/client/aura_constants.h"
- #include "ui/aura/scoped_window_event_targeting_blocker.h"
- #include "ui/aura/window.h"
- #include "ui/aura/window_event_dispatcher.h"
- #include "ui/aura/window_observer.h"
- #include "ui/aura/window_tree_host.h"
- #include "ui/base/class_property.h"
- #include "ui/compositor/compositor.h"
- #include "ui/compositor/compositor_lock.h"
- #include "ui/compositor/layer.h"
- #include "ui/display/display.h"
- #include "ui/display/screen.h"
- #include "ui/display/tablet_state.h"
- #include "ui/gfx/geometry/point.h"
- #include "ui/gfx/geometry/size.h"
- #include "ui/views/widget/widget.h"
- #include "ui/wm/core/coordinate_conversion.h"
- #include "ui/wm/core/window_util.h"
- DEFINE_UI_CLASS_PROPERTY_TYPE(exo::ClientControlledShellSurface*)
- namespace exo {
- namespace {
- using ::ash::screen_util::GetIdealBoundsForMaximizedOrFullscreenOrPinnedState;
- using ::chromeos::WindowStateType;
- // Client controlled specific accelerators.
- const struct {
- ui::KeyboardCode keycode;
- int modifiers;
- ClientControlledAcceleratorAction action;
- } kAccelerators[] = {
- {ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN,
- ClientControlledAcceleratorAction::ZOOM_OUT},
- {ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN,
- ClientControlledAcceleratorAction::ZOOM_IN},
- {ui::VKEY_0, ui::EF_CONTROL_DOWN,
- ClientControlledAcceleratorAction::ZOOM_RESET},
- };
- ClientControlledShellSurface::DelegateFactoryCallback& GetFactoryForTesting() {
- using CallbackType = ClientControlledShellSurface::DelegateFactoryCallback;
- static base::NoDestructor<CallbackType> factory;
- return *factory;
- }
- // Maximum amount of time to wait for contents that match the display's
- // orientation in tablet mode.
- // TODO(oshima): Looks like android is generating unnecessary frames.
- // Fix it on Android side and reduce the timeout.
- constexpr int kOrientationLockTimeoutMs = 2500;
- Orientation SizeToOrientation(const gfx::Size& size) {
- DCHECK_NE(size.width(), size.height());
- return size.width() > size.height() ? Orientation::LANDSCAPE
- : Orientation::PORTRAIT;
- }
- // A ClientControlledStateDelegate that sends the state/bounds
- // change request to exo client.
- class ClientControlledStateDelegate
- : public ash::ClientControlledState::Delegate {
- public:
- explicit ClientControlledStateDelegate(
- ClientControlledShellSurface* shell_surface)
- : shell_surface_(shell_surface) {}
- ClientControlledStateDelegate(const ClientControlledStateDelegate&) = delete;
- ClientControlledStateDelegate& operator=(
- const ClientControlledStateDelegate&) = delete;
- ~ClientControlledStateDelegate() override {}
- // Overridden from ash::ClientControlledState::Delegate:
- void HandleWindowStateRequest(ash::WindowState* window_state,
- chromeos::WindowStateType next_state) override {
- shell_surface_->OnWindowStateChangeEvent(window_state->GetStateType(),
- next_state);
- }
- void HandleBoundsRequest(ash::WindowState* window_state,
- chromeos::WindowStateType requested_state,
- const gfx::Rect& bounds_in_display,
- int64_t display_id) override {
- shell_surface_->OnBoundsChangeEvent(
- window_state->GetStateType(), requested_state, display_id,
- bounds_in_display,
- window_state->drag_details() && shell_surface_->IsDragging()
- ? window_state->drag_details()->bounds_change
- : 0);
- }
- private:
- ClientControlledShellSurface* shell_surface_;
- };
- // A WindowStateDelegate that implements ToggleFullscreen behavior for
- // client controlled window.
- class ClientControlledWindowStateDelegate : public ash::WindowStateDelegate {
- public:
- explicit ClientControlledWindowStateDelegate(
- ClientControlledShellSurface* shell_surface,
- ash::ClientControlledState::Delegate* delegate)
- : shell_surface_(shell_surface), delegate_(delegate) {}
- ClientControlledWindowStateDelegate(
- const ClientControlledWindowStateDelegate&) = delete;
- ClientControlledWindowStateDelegate& operator=(
- const ClientControlledWindowStateDelegate&) = delete;
- ~ClientControlledWindowStateDelegate() override {}
- // Overridden from ash::WindowStateDelegate:
- bool ToggleFullscreen(ash::WindowState* window_state) override {
- chromeos::WindowStateType next_state;
- aura::Window* window = window_state->window();
- switch (window_state->GetStateType()) {
- case chromeos::WindowStateType::kDefault:
- case chromeos::WindowStateType::kNormal:
- next_state = chromeos::WindowStateType::kFullscreen;
- break;
- case chromeos::WindowStateType::kMaximized:
- next_state = chromeos::WindowStateType::kFullscreen;
- break;
- case chromeos::WindowStateType::kFullscreen:
- switch (window->GetProperty(aura::client::kRestoreShowStateKey)) {
- case ui::SHOW_STATE_DEFAULT:
- case ui::SHOW_STATE_NORMAL:
- next_state = chromeos::WindowStateType::kNormal;
- break;
- case ui::SHOW_STATE_MAXIMIZED:
- next_state = chromeos::WindowStateType::kMaximized;
- break;
- case ui::SHOW_STATE_MINIMIZED:
- next_state = chromeos::WindowStateType::kMinimized;
- break;
- case ui::SHOW_STATE_FULLSCREEN:
- case ui::SHOW_STATE_INACTIVE:
- case ui::SHOW_STATE_END:
- NOTREACHED() << " unknown state :"
- << window->GetProperty(
- aura::client::kRestoreShowStateKey);
- return false;
- }
- break;
- case chromeos::WindowStateType::kMinimized: {
- next_state = chromeos::WindowStateType::kFullscreen;
- break;
- }
- default:
- // TODO(oshima|xdai): Handle SNAP state.
- return false;
- }
- delegate_->HandleWindowStateRequest(window_state, next_state);
- return true;
- }
- void ToggleLockedFullscreen(ash::WindowState*) override {
- // No special handling for locked ARC windows.
- return;
- }
- std::unique_ptr<ash::PresentationTimeRecorder> OnDragStarted(
- int component) override {
- shell_surface_->OnDragStarted(component);
- return nullptr;
- }
- void OnDragFinished(bool canceled, const gfx::PointF& location) override {
- shell_surface_->OnDragFinished(canceled, location);
- }
- private:
- ClientControlledShellSurface* shell_surface_;
- ash::ClientControlledState::Delegate* delegate_;
- };
- bool IsPinned(const ash::WindowState* window_state) {
- return window_state->IsPinned() || window_state->IsTrustedPinned();
- }
- class CaptionButtonModel : public chromeos::CaptionButtonModel {
- public:
- CaptionButtonModel(uint32_t visible_button_mask, uint32_t enabled_button_mask)
- : visible_button_mask_(visible_button_mask),
- enabled_button_mask_(enabled_button_mask) {}
- CaptionButtonModel(const CaptionButtonModel&) = delete;
- CaptionButtonModel& operator=(const CaptionButtonModel&) = delete;
- // Overridden from ash::CaptionButtonModel:
- bool IsVisible(views::CaptionButtonIcon icon) const override {
- return visible_button_mask_ & (1 << icon);
- }
- bool IsEnabled(views::CaptionButtonIcon icon) const override {
- return enabled_button_mask_ & (1 << icon);
- }
- bool InZoomMode() const override {
- return visible_button_mask_ & (1 << views::CAPTION_BUTTON_ICON_ZOOM);
- }
- private:
- uint32_t visible_button_mask_;
- uint32_t enabled_button_mask_;
- };
- // EventTargetingBlocker blocks the event targeting by setting NONE targeting
- // policy to the window subtrees. It resets to the original policy upon
- // deletion.
- class EventTargetingBlocker : aura::WindowObserver {
- public:
- EventTargetingBlocker() = default;
- EventTargetingBlocker(const EventTargetingBlocker&) = delete;
- EventTargetingBlocker& operator=(const EventTargetingBlocker&) = delete;
- ~EventTargetingBlocker() override {
- if (window_)
- Unregister(window_);
- }
- void Block(aura::Window* window) {
- window_ = window;
- Register(window);
- }
- private:
- void Register(aura::Window* window) {
- window->AddObserver(this);
- event_targeting_blocker_map_[window] =
- std::make_unique<aura::ScopedWindowEventTargetingBlocker>(window);
- for (auto* child : window->children())
- Register(child);
- }
- void Unregister(aura::Window* window) {
- window->RemoveObserver(this);
- event_targeting_blocker_map_.erase(window);
- for (auto* child : window->children())
- Unregister(child);
- }
- void OnWindowDestroying(aura::Window* window) override {
- Unregister(window);
- if (window_ == window)
- window_ = nullptr;
- }
- std::map<aura::Window*,
- std::unique_ptr<aura::ScopedWindowEventTargetingBlocker>>
- event_targeting_blocker_map_;
- aura::Window* window_ = nullptr;
- };
- } // namespace
- class ClientControlledShellSurface::ScopedSetBoundsLocally {
- public:
- explicit ScopedSetBoundsLocally(ClientControlledShellSurface* shell_surface)
- : state_(shell_surface->client_controlled_state_) {
- state_->set_bounds_locally(true);
- }
- ScopedSetBoundsLocally(const ScopedSetBoundsLocally&) = delete;
- ScopedSetBoundsLocally& operator=(const ScopedSetBoundsLocally&) = delete;
- ~ScopedSetBoundsLocally() { state_->set_bounds_locally(false); }
- private:
- ash::ClientControlledState* const state_;
- };
- class ClientControlledShellSurface::ScopedLockedToRoot {
- public:
- explicit ScopedLockedToRoot(views::Widget* widget)
- : window_(widget->GetNativeWindow()) {
- window_->SetProperty(ash::kLockedToRootKey, true);
- }
- ScopedLockedToRoot(const ScopedLockedToRoot&) = delete;
- ScopedLockedToRoot& operator=(const ScopedLockedToRoot&) = delete;
- ~ScopedLockedToRoot() { window_->ClearProperty(ash::kLockedToRootKey); }
- private:
- aura::Window* const window_;
- };
- ////////////////////////////////////////////////////////////////////////////////
- // ClientControlledShellSurface, public:
- ClientControlledShellSurface::ClientControlledShellSurface(
- Surface* surface,
- bool can_minimize,
- int container,
- bool default_scale_cancellation)
- : ShellSurfaceBase(surface, gfx::Point(), can_minimize, container),
- use_default_scale_cancellation_(default_scale_cancellation) {
- server_side_resize_ = true;
- }
- ClientControlledShellSurface::~ClientControlledShellSurface() {
- // Reset the window delegate here so that we won't try to do any dragging
- // operation on a to-be-destroyed window. |widget_| can be nullptr in tests.
- if (GetWidget())
- GetWindowState()->SetDelegate(nullptr);
- if (client_controlled_state_)
- client_controlled_state_->ResetDelegate();
- wide_frame_.reset();
- }
- void ClientControlledShellSurface::SetBounds(int64_t display_id,
- const gfx::Rect& bounds) {
- TRACE_EVENT2("exo", "ClientControlledShellSurface::SetBounds", "display_id",
- display_id, "bounds", bounds.ToString());
- if (bounds.IsEmpty()) {
- DLOG(WARNING) << "Bounds must be non-empty";
- return;
- }
- // When use_default_scale_cancellation_ is false, the client is scale-aware
- // and we expect that the |bounds| has been calculated by the client based
- // on the device_scale_factor of the display with |display_id|.
- // If the display has been changed before |SetBounds()| is called, for some
- // cases(eg. move ARC window between displays with shortcut), |pending_scale_|
- // may be stale and tied the old pending_display. Therefore, we re-initialize
- // it to 0.0 here to force an update on the value in |EnsurePendingScale()|.
- // Also need to note that we only want to commit |pending_scale_| in the cases
- // where it hasn't been initialized before this method call.
- bool const commit_immediately = pending_scale_ == 0.0;
- if (!use_default_scale_cancellation_ && display_id != pending_display_id_)
- pending_scale_ = 0.0;
- SetDisplay(display_id);
- EnsurePendingScale(commit_immediately);
- const gfx::Rect bounds_dp =
- gfx::ScaleToRoundedRect(bounds, GetClientToDpPendingScale());
- SetGeometry(bounds_dp);
- }
- void ClientControlledShellSurface::SetBoundsOrigin(int64_t display_id,
- const gfx::Point& origin) {
- TRACE_EVENT2("exo", "ClientControlledShellSurface::SetBoundsOrigin",
- "display_id", display_id, "origin", origin.ToString());
- SetDisplay(display_id);
- EnsurePendingScale(/*commit_immediately=*/true);
- const gfx::Point origin_dp =
- gfx::ScaleToRoundedPoint(origin, GetClientToDpPendingScale());
- pending_geometry_.set_origin(origin_dp);
- }
- void ClientControlledShellSurface::SetBoundsSize(const gfx::Size& size) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetBoundsSize", "size",
- size.ToString());
- if (size.IsEmpty()) {
- DLOG(WARNING) << "Bounds size must be non-empty";
- return;
- }
- EnsurePendingScale(/*commit_immediately=*/true);
- const gfx::Size size_dp =
- gfx::ScaleToRoundedSize(size, GetClientToDpPendingScale());
- pending_geometry_.set_size(size_dp);
- }
- void ClientControlledShellSurface::SetMaximized() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::SetMaximized");
- pending_window_state_ = chromeos::WindowStateType::kMaximized;
- }
- void ClientControlledShellSurface::SetMinimized() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::SetMinimized");
- pending_window_state_ = chromeos::WindowStateType::kMinimized;
- }
- void ClientControlledShellSurface::SetRestored() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::SetRestored");
- pending_window_state_ = chromeos::WindowStateType::kNormal;
- }
- void ClientControlledShellSurface::SetFullscreen(bool fullscreen) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetFullscreen",
- "fullscreen", fullscreen);
- pending_window_state_ = fullscreen ? chromeos::WindowStateType::kFullscreen
- : chromeos::WindowStateType::kNormal;
- }
- void ClientControlledShellSurface::SetPinned(chromeos::WindowPinType type) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetPinned", "type",
- static_cast<int>(type));
- if (!widget_)
- CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
- if (type == chromeos::WindowPinType::kNone) {
- ash::WindowState::Get(widget_->GetNativeWindow())->Restore();
- } else {
- bool trusted = type == chromeos::WindowPinType::kTrustedPinned;
- ash::window_util::PinWindow(widget_->GetNativeWindow(), trusted);
- }
- }
- void ClientControlledShellSurface::SetSystemUiVisibility(bool autohide) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetSystemUiVisibility",
- "autohide", autohide);
- if (!widget_)
- CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
- ash::window_util::SetAutoHideShelf(widget_->GetNativeWindow(), autohide);
- }
- void ClientControlledShellSurface::SetAlwaysOnTop(bool always_on_top) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetAlwaysOnTop",
- "always_on_top", always_on_top);
- pending_always_on_top_ = always_on_top;
- }
- void ClientControlledShellSurface::SetOrientation(Orientation orientation) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetOrientation",
- "orientation",
- orientation == Orientation::PORTRAIT ? "portrait" : "landscape");
- pending_orientation_ = orientation;
- }
- void ClientControlledShellSurface::SetShadowBounds(const gfx::Rect& bounds) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetShadowBounds", "bounds",
- bounds.ToString());
- auto shadow_bounds =
- bounds.IsEmpty() ? absl::nullopt : absl::make_optional(bounds);
- if (shadow_bounds_ != shadow_bounds) {
- shadow_bounds_ = shadow_bounds;
- shadow_bounds_changed_ = true;
- }
- }
- void ClientControlledShellSurface::SetScale(double scale) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetScale", "scale", scale);
- if (scale <= 0.0) {
- DLOG(WARNING) << "Surface scale must be greater than 0";
- return;
- }
- pending_scale_ = scale;
- }
- void ClientControlledShellSurface::CommitPendingScale() {
- if (pending_scale_ == scale_ || pending_scale_ == 0.0)
- return;
- gfx::Transform transform;
- transform.Scale(1.0 / pending_scale_, 1.0 / pending_scale_);
- host_window()->SetTransform(transform);
- scale_ = pending_scale_;
- UpdateCornerRadius();
- }
- void ClientControlledShellSurface::SetTopInset(int height) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetTopInset", "height",
- height);
- pending_top_inset_height_ = height;
- }
- void ClientControlledShellSurface::OnWindowStateChangeEvent(
- chromeos::WindowStateType current_state,
- chromeos::WindowStateType next_state) {
- // Android already knows this state change. Don't send state change to Android
- // that it is about to do anyway.
- if (delegate_ && pending_window_state_ != next_state)
- delegate_->OnStateChanged(current_state, next_state);
- }
- void ClientControlledShellSurface::StartDrag(int component,
- const gfx::PointF& location) {
- TRACE_EVENT2("exo", "ClientControlledShellSurface::StartDrag", "component",
- component, "location", location.ToString());
- if (!widget_)
- return;
- AttemptToStartDrag(component, location);
- }
- void ClientControlledShellSurface::AttemptToStartDrag(
- int component,
- const gfx::PointF& location) {
- aura::Window* target = widget_->GetNativeWindow();
- ash::ToplevelWindowEventHandler* toplevel_handler =
- ash::Shell::Get()->toplevel_window_event_handler();
- aura::Window* mouse_pressed_handler =
- target->GetHost()->dispatcher()->mouse_pressed_handler();
- // Start dragging only if ...
- // 1) touch guesture is in progres.
- // 2) mouse was pressed on the target or its subsurfaces.
- if (toplevel_handler->gesture_target() ||
- (mouse_pressed_handler && target->Contains(mouse_pressed_handler))) {
- gfx::PointF point_in_root(location);
- if (use_default_scale_cancellation_) {
- // When default scale cancellation is enabled, the client sends the
- // location in screen coordinates. Otherwise, the location should already
- // be in the display's coordinates.
- wm::ConvertPointFromScreen(target->GetRootWindow(), &point_in_root);
- }
- toplevel_handler->AttemptToStartDrag(
- target, point_in_root, component,
- ash::ToplevelWindowEventHandler::EndClosure());
- }
- }
- bool ClientControlledShellSurface::IsDragging() {
- return in_drag_;
- }
- void ClientControlledShellSurface::SetCanMaximize(bool can_maximize) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetCanMaximize",
- "can_maximzie", can_maximize);
- can_maximize_ = can_maximize;
- if (widget_)
- widget_->OnSizeConstraintsChanged();
- }
- void ClientControlledShellSurface::UpdateAutoHideFrame() {
- if (immersive_fullscreen_controller_) {
- bool enabled = (frame_type_ == SurfaceFrameType::AUTOHIDE &&
- (GetWindowState()->IsMaximizedOrFullscreenOrPinned() ||
- GetWindowState()->IsSnapped()));
- chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, enabled);
- }
- }
- void ClientControlledShellSurface::SetFrameButtons(
- uint32_t visible_button_mask,
- uint32_t enabled_button_mask) {
- if (frame_visible_button_mask_ == visible_button_mask &&
- frame_enabled_button_mask_ == enabled_button_mask) {
- return;
- }
- frame_visible_button_mask_ = visible_button_mask;
- frame_enabled_button_mask_ = enabled_button_mask;
- if (widget_)
- UpdateCaptionButtonModel();
- }
- void ClientControlledShellSurface::SetExtraTitle(
- const std::u16string& extra_title) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetExtraTitle",
- "extra_title", base::UTF16ToUTF8(extra_title));
- if (!widget_) {
- initial_extra_title_ = extra_title;
- return;
- }
- GetFrameView()->GetHeaderView()->GetFrameHeader()->SetFrameTextOverride(
- extra_title);
- if (wide_frame_) {
- wide_frame_->header_view()->GetFrameHeader()->SetFrameTextOverride(
- extra_title);
- }
- }
- void ClientControlledShellSurface::SetClientAccessibilityId(
- int32_t accessibility_id) {
- if (accessibility_id >= 0)
- client_accessibility_id_ = accessibility_id;
- else
- client_accessibility_id_.reset();
- if (widget_ && widget_->GetNativeWindow()) {
- SetShellClientAccessibilityId(widget_->GetNativeWindow(),
- client_accessibility_id_);
- }
- }
- void ClientControlledShellSurface::RebindRootSurface(
- Surface* root_surface,
- bool can_minimize,
- int container,
- bool default_scale_cancellation) {
- use_default_scale_cancellation_ = default_scale_cancellation;
- ShellSurfaceBase::RebindRootSurface(root_surface, can_minimize, container);
- }
- void ClientControlledShellSurface::DidReceiveCompositorFrameAck() {
- orientation_ = pending_orientation_;
- // Unlock the compositor after the frame is received by viz so that
- // screenshot contain the correct frame.
- if (expected_orientation_ == orientation_)
- orientation_compositor_lock_.reset();
- SurfaceTreeHost::DidReceiveCompositorFrameAck();
- }
- void ClientControlledShellSurface::OnBoundsChangeEvent(
- chromeos::WindowStateType current_state,
- chromeos::WindowStateType requested_state,
- int64_t display_id,
- const gfx::Rect& window_bounds,
- int bounds_change) {
- if (ignore_bounds_change_request_)
- return;
- // 1) Do no update the bounds unless we have geometry from client.
- // 2) Do not update the bounds if window is minimized unless it
- // exiting the minimzied state.
- // The bounds will be provided by client when unminimized.
- if (geometry().IsEmpty() || window_bounds.IsEmpty() ||
- (widget_->IsMinimized() &&
- requested_state == chromeos::WindowStateType::kMinimized) ||
- !delegate_) {
- return;
- }
- // Sends the client bounds, which matches the geometry
- // when frame is enabled.
- const gfx::Rect client_bounds = GetClientBoundsForWindowBoundsAndWindowState(
- window_bounds, requested_state);
- gfx::Size current_size = GetFrameView()->GetBoundsForClientView().size();
- bool is_resize = client_bounds.size() != current_size &&
- !widget_->IsMaximized() && !widget_->IsFullscreen();
- // Make sure to use the up-to-date scale factor. At this point, |scale_| or
- // |pending_scale_| may not be updated yet.
- display::Display display;
- const bool display_exists =
- display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id,
- &display);
- DCHECK(display_exists && display.is_valid());
- const float scale =
- use_default_scale_cancellation_ ? 1.f : display.device_scale_factor();
- const gfx::Rect scaled_client_bounds =
- gfx::ScaleToRoundedRect(client_bounds, scale);
- delegate_->OnBoundsChanged(current_state, requested_state, display_id,
- scaled_client_bounds, is_resize, bounds_change);
- auto* window_state = GetWindowState();
- if (server_reparent_window_ &&
- window_state->GetDisplay().id() != display_id) {
- ScopedSetBoundsLocally scoped_set_bounds(this);
- int container_id = window_state->window()->parent()->GetId();
- aura::Window* new_parent =
- ash::Shell::GetRootWindowControllerWithDisplayId(display_id)
- ->GetContainer(container_id);
- new_parent->AddChild(window_state->window());
- }
- }
- void ClientControlledShellSurface::ChangeZoomLevel(ZoomChange change) {
- if (delegate_)
- delegate_->OnZoomLevelChanged(change);
- }
- void ClientControlledShellSurface::OnDragStarted(int component) {
- in_drag_ = true;
- if (delegate_)
- delegate_->OnDragStarted(component);
- }
- void ClientControlledShellSurface::OnDragFinished(bool canceled,
- const gfx::PointF& location) {
- in_drag_ = false;
- if (!delegate_)
- return;
- const float scale = 1.f / GetClientToDpScale();
- const gfx::PointF scaled = gfx::ScalePoint(location, scale);
- delegate_->OnDragFinished(scaled.x(), scaled.y(), canceled);
- }
- float ClientControlledShellSurface::GetClientToDpScale() const {
- // If the default_device_scale_factor is used for scale cancellation,
- // we expect the client will already send bounds in DP.
- if (use_default_scale_cancellation_)
- return 1.f;
- return 1.f / scale_;
- }
- void ClientControlledShellSurface::SetResizeLockType(
- ash::ArcResizeLockType resize_lock_type) {
- TRACE_EVENT1("exo", "ClientControlledShellSurface::SetResizeLockType",
- "resize_lock_type", resize_lock_type);
- pending_resize_lock_type_ = resize_lock_type;
- }
- void ClientControlledShellSurface::UpdateResizability() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::updateCanResize");
- widget_->GetNativeWindow()->SetProperty(ash::kArcResizeLockTypeKey,
- pending_resize_lock_type_);
- // If resize lock is enabled, the window is explicitly marded as unresizable.
- // Otherwise, the decision is deferred to the parent class.
- if (pending_resize_lock_type_ ==
- ash::ArcResizeLockType::RESIZE_DISABLED_TOGGLABLE ||
- pending_resize_lock_type_ ==
- ash::ArcResizeLockType::RESIZE_DISABLED_NONTOGGLABLE) {
- SetCanResize(false);
- return;
- }
- ShellSurfaceBase::UpdateResizability();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // SurfaceDelegate overrides:
- bool ClientControlledShellSurface::IsInputEnabled(Surface* surface) const {
- // Client-driven dragging/resizing relies on implicit grab, which ensures that
- // mouse/touch events are delivered to the focused surface until release, even
- // if they fall outside surface bounds. However, if the client destroys the
- // surface with implicit grab, the drag/resize is prematurely ended. Prevent
- // this by delivering all input events to the root surface, which shares the
- // lifetime of the shell surface.
- // TODO(domlaskowski): Remove once the client is provided with an API to hook
- // into server-driven dragging/resizing.
- return surface == root_surface();
- }
- void ClientControlledShellSurface::OnSetFrame(SurfaceFrameType type) {
- pending_frame_type_ = type;
- }
- void ClientControlledShellSurface::OnSetFrameColors(SkColor active_color,
- SkColor inactive_color) {
- ShellSurfaceBase::OnSetFrameColors(active_color, inactive_color);
- if (wide_frame_) {
- aura::Window* window = wide_frame_->GetWidget()->GetNativeWindow();
- window->SetProperty(chromeos::kTrackDefaultFrameColors, false);
- window->SetProperty(chromeos::kFrameActiveColorKey, active_color);
- window->SetProperty(chromeos::kFrameInactiveColorKey, inactive_color);
- }
- }
- void ClientControlledShellSurface::SetSnappedToPrimary() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::SetSnappedToPrimary");
- pending_window_state_ = chromeos::WindowStateType::kPrimarySnapped;
- }
- void ClientControlledShellSurface::SetSnappedToSecondary() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::SetSnappedToSecondary");
- pending_window_state_ = chromeos::WindowStateType::kSecondarySnapped;
- }
- void ClientControlledShellSurface::SetPip() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::SetPip");
- pending_window_state_ = chromeos::WindowStateType::kPip;
- }
- void ClientControlledShellSurface::UnsetPip() {
- TRACE_EVENT0("exo", "ClientControlledShellSurface::UnsetPip");
- SetRestored();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // aura::WindowObserver overrides:
- void ClientControlledShellSurface::OnWindowAddedToRootWindow(
- aura::Window* window) {
- // Window dragging across display moves the window to target display when
- // dropped, but the actual window bounds comes later from android. Update the
- // window bounds now so that the window stays where it is expected to be. (it
- // may still move if the android sends different bounds).
- if (client_controlled_state_->set_bounds_locally() ||
- !GetWindowState()->is_dragged()) {
- return;
- }
- ScopedLockedToRoot scoped_locked_to_root(widget_);
- UpdateWidgetBounds();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // views::WidgetDelegate overrides:
- bool ClientControlledShellSurface::CanMaximize() const {
- return can_maximize_;
- }
- std::unique_ptr<views::NonClientFrameView>
- ClientControlledShellSurface::CreateNonClientFrameView(views::Widget* widget) {
- ash::WindowState* window_state = GetWindowState();
- std::unique_ptr<ash::ClientControlledState::Delegate> delegate =
- GetFactoryForTesting()
- ? GetFactoryForTesting().Run()
- : std::make_unique<ClientControlledStateDelegate>(this);
- auto window_delegate = std::make_unique<ClientControlledWindowStateDelegate>(
- this, delegate.get());
- auto state =
- std::make_unique<ash::ClientControlledState>(std::move(delegate));
- client_controlled_state_ = state.get();
- window_state->SetStateObject(std::move(state));
- window_state->SetDelegate(std::move(window_delegate));
- auto frame_view = CreateNonClientFrameViewInternal(widget);
- immersive_fullscreen_controller_ =
- std::make_unique<chromeos::ImmersiveFullscreenController>();
- static_cast<ash::NonClientFrameViewAsh*>(frame_view.get())
- ->InitImmersiveFullscreenControllerForView(
- immersive_fullscreen_controller_.get());
- return frame_view;
- }
- bool ClientControlledShellSurface::ShouldSaveWindowPlacement() const {
- return false;
- }
- void ClientControlledShellSurface::SaveWindowPlacement(
- const gfx::Rect& bounds,
- ui::WindowShowState show_state) {}
- bool ClientControlledShellSurface::GetSavedWindowPlacement(
- const views::Widget* widget,
- gfx::Rect* bounds,
- ui::WindowShowState* show_state) const {
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // views::View overrides:
- gfx::Size ClientControlledShellSurface::GetMaximumSize() const {
- if (can_maximize_) {
- // On ChromeOS, a window with non empty maximum size is non-maximizable,
- // even if CanMaximize() returns true. ClientControlledShellSurface
- // sololy depends on |can_maximize_| to determine if it is maximizable,
- // so just return empty size.
- return gfx::Size();
- } else {
- return ShellSurfaceBase::GetMaximumSize();
- }
- }
- void ClientControlledShellSurface::OnDeviceScaleFactorChanged(float old_dsf,
- float new_dsf) {
- if (!use_default_scale_cancellation_) {
- SetScale(new_dsf);
- // Commit scale changes immediately if we expect that the window will not be
- // resized.
- if (widget_->IsMaximized() || widget_->IsFullscreen() ||
- WMHelper::GetInstance()->InTabletMode())
- CommitPendingScale();
- }
- views::View::OnDeviceScaleFactorChanged(old_dsf, new_dsf);
- UpdateFrameWidth();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // display::DisplayObserver overrides:
- void ClientControlledShellSurface::OnDisplayMetricsChanged(
- const display::Display& new_display,
- uint32_t changed_metrics) {
- SurfaceTreeHost::OnDisplayMetricsChanged(new_display, changed_metrics);
- if (!widget_)
- return;
- // The PIP window bounds is adjusted in Ash when the screen is rotated, but
- // Android has an obsolete bounds for a while and applies it incorrectly.
- // We need to ignore those bounds change until the states are completely
- // synced on both sides.
- if (GetWindowState()->IsPip() &&
- changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION) {
- gfx::Rect bounds_after_rotation =
- ash::PipPositioner::GetSnapFractionAppliedBounds(GetWindowState());
- display_rotating_with_pip_ =
- bounds_after_rotation !=
- GetWindowState()->window()->GetBoundsInScreen();
- }
- const display::Screen* screen = display::Screen::GetScreen();
- display::Display current_display =
- screen->GetDisplayNearestWindow(widget_->GetNativeWindow());
- if (current_display.id() != new_display.id())
- return;
- bool in_tablet_mode = WMHelper::GetInstance()->InTabletMode();
- if (!use_default_scale_cancellation_ &&
- changed_metrics &
- display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR) {
- SetScale(new_display.device_scale_factor());
- // Commit scale changes immediately if we expect that the window will not be
- // resized.
- if (widget_->IsMaximized() || widget_->IsFullscreen() || in_tablet_mode)
- CommitPendingScale();
- }
- if (!in_tablet_mode || !widget_->IsActive() ||
- !(changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)) {
- return;
- }
- Orientation target_orientation = SizeToOrientation(new_display.size());
- if (orientation_ == target_orientation)
- return;
- expected_orientation_ = target_orientation;
- EnsureCompositorIsLockedForOrientationChange();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ui::CompositorLockClient overrides:
- void ClientControlledShellSurface::CompositorLockTimedOut() {
- orientation_compositor_lock_.reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ShellSurfaceBase overrides:
- void ClientControlledShellSurface::SetSystemModal(bool system_modal) {
- // System modal container is used by clients to implement client side
- // managed system modal dialogs using a single ShellSurface instance.
- // Hit-test region will be non-empty when at least one dialog exists on
- // the client side. Here we detect the transition between no client side
- // dialog and at least one dialog so activatable state is properly
- // updated.
- if (container_ != ash::kShellWindowId_SystemModalContainer) {
- LOG(ERROR)
- << "Only a window in SystemModalContainer can change the modality";
- return;
- }
- ShellSurfaceBase::SetSystemModal(system_modal);
- }
- void ClientControlledShellSurface::SetWidgetBounds(const gfx::Rect& bounds,
- bool adjusted_by_server) {
- const auto* screen = display::Screen::GetScreen();
- aura::Window* window = widget_->GetNativeWindow();
- display::Display current_display = screen->GetDisplayNearestWindow(window);
- bool is_display_move_pending = false;
- display::Display target_display = current_display;
- display::Display display;
- if (screen->GetDisplayWithDisplayId(display_id_, &display)) {
- bool is_display_stale = display_id_ != current_display.id();
- // Preserve widget bounds until client acknowledges display move.
- if (preserve_widget_bounds_ && is_display_stale)
- return;
- // True if the window has just been reparented to another root window, and
- // the move was initiated by the server.
- // TODO(oshima): Improve the window moving logic. https://crbug.com/875047
- is_display_move_pending =
- window->GetProperty(ash::kLockedToRootKey) && is_display_stale;
- if (!is_display_move_pending)
- target_display = display;
- preserve_widget_bounds_ = is_display_move_pending;
- } else {
- preserve_widget_bounds_ = false;
- }
- if (!use_default_scale_cancellation_) {
- bool needs_initial_commit = pending_scale_ == 0.0;
- SetScale(current_display.device_scale_factor());
- if (needs_initial_commit)
- CommitPendingScale();
- }
- // Calculate a minimum window visibility required bounds.
- // TODO(oshima): Move this to ComputeAdjustedBounds.
- gfx::Rect adjusted_bounds = bounds;
- if (!is_display_move_pending) {
- const gfx::Rect& restriction = GetWindowState()->IsFullscreen()
- ? target_display.bounds()
- : target_display.work_area();
- ash::ClientControlledState::AdjustBoundsForMinimumWindowVisibility(
- restriction, &adjusted_bounds);
- // Collision detection to the bounds set by Android should be applied only
- // to initial bounds. Do not adjust new bounds as it can be obsolete or in
- // transit during animation, which results in incorrect resting postiion.
- // The resting position should be fully controlled by chrome afterwards
- // because Android isn't aware of Chrome OS System UI.
- if (GetWindowState()->IsPip() &&
- !ash::PipPositioner::HasSnapFraction(GetWindowState())) {
- adjusted_bounds = ash::CollisionDetectionUtils::GetRestingPosition(
- target_display, adjusted_bounds,
- ash::CollisionDetectionUtils::RelativePriority::kPictureInPicture);
- }
- }
- if (adjusted_bounds == widget_->GetWindowBoundsInScreen() &&
- target_display.id() == current_display.id()) {
- return;
- }
- bool set_bounds_locally =
- display_rotating_with_pip_ ||
- (GetWindowState()->is_dragged() && !is_display_move_pending);
- if (set_bounds_locally || client_controlled_state_->set_bounds_locally()) {
- // Convert from screen to display coordinates.
- gfx::Point origin = bounds.origin();
- wm::ConvertPointFromScreen(window->parent(), &origin);
- // Move the window relative to the current display.
- {
- ScopedSetBoundsLocally scoped_set_bounds(this);
- window->SetBounds(gfx::Rect(origin, adjusted_bounds.size()));
- }
- UpdateSurfaceBounds();
- return;
- }
- {
- ScopedSetBoundsLocally scoped_set_bounds(this);
- window->SetBoundsInScreen(adjusted_bounds, target_display);
- }
- if (bounds != adjusted_bounds || is_display_move_pending) {
- // Notify client that bounds were adjusted or window moved across displays.
- auto state_type = GetWindowState()->GetStateType();
- gfx::Rect adjusted_bounds_in_display(adjusted_bounds);
- adjusted_bounds_in_display.Offset(
- -target_display.bounds().OffsetFromOrigin());
- OnBoundsChangeEvent(state_type, state_type, target_display.id(),
- adjusted_bounds_in_display, 0);
- }
- UpdateSurfaceBounds();
- }
- gfx::Rect ClientControlledShellSurface::GetShadowBounds() const {
- gfx::Rect shadow_bounds = ShellSurfaceBase::GetShadowBounds();
- const ash::NonClientFrameViewAsh* frame_view = GetFrameView();
- if (frame_view->GetFrameEnabled() && !shadow_bounds_->IsEmpty() &&
- !geometry_.IsEmpty()) {
- // The client controlled geometry is only for the client
- // area. When the chrome side frame is enabled, the shadow height
- // has to include the height of the frame, and the total height is
- // equals to the window height computed by
- // |GetWindowBoundsForClientBounds|.
- shadow_bounds.set_size(
- frame_view->GetWindowBoundsForClientBounds(shadow_bounds).size());
- }
- return shadow_bounds;
- }
- void ClientControlledShellSurface::InitializeWindowState(
- ash::WindowState* window_state) {
- // Allow the client to request bounds that do not fill the entire work area
- // when maximized, or the entire display when fullscreen.
- window_state->set_allow_set_bounds_direct(true);
- window_state->set_ignore_keyboard_bounds_change(true);
- if (container_ == ash::kShellWindowId_SystemModalContainer ||
- container_ == ash::kShellWindowId_ArcVirtualKeyboardContainer) {
- DisableMovement();
- }
- ash::NonClientFrameViewAsh* frame_view = GetFrameView();
- frame_view->SetCaptionButtonModel(std::make_unique<CaptionButtonModel>(
- frame_visible_button_mask_, frame_enabled_button_mask_));
- UpdateAutoHideFrame();
- UpdateFrameWidth();
- if (initial_orientation_lock_ != chromeos::OrientationType::kAny)
- SetOrientationLock(initial_orientation_lock_);
- if (initial_extra_title_ != std::u16string())
- SetExtraTitle(initial_extra_title_);
- // Register Client controlled accelerators.
- views::FocusManager* focus_manager = widget_->GetFocusManager();
- accelerator_target_ =
- std::make_unique<ClientControlledAcceleratorTarget>(this);
- // These shortcuts are same as ones used in chrome.
- // TODO: investigate if we need to reassign.
- for (const auto& entry : kAccelerators) {
- focus_manager->RegisterAccelerator(
- ui::Accelerator(entry.keycode, entry.modifiers),
- ui::AcceleratorManager::kNormalPriority, accelerator_target_.get());
- accelerator_target_->RegisterAccelerator(
- ui::Accelerator(entry.keycode, entry.modifiers), entry.action);
- }
- auto* window = widget_->GetNativeWindow();
- SetShellClientAccessibilityId(window, client_accessibility_id_);
- GrantPermissionToActivateIndefinitely(window);
- }
- float ClientControlledShellSurface::GetScale() const {
- return scale_;
- }
- absl::optional<gfx::Rect> ClientControlledShellSurface::GetWidgetBounds()
- const {
- const ash::NonClientFrameViewAsh* frame_view = GetFrameView();
- if (frame_view->GetFrameEnabled()) {
- gfx::Rect visible_bounds = GetVisibleBounds();
- if (widget_->IsMaximized() && frame_type_ == SurfaceFrameType::NORMAL) {
- // When the widget is maximized in clamshell mode, client sends
- // |geometry_| without taking caption height into account.
- visible_bounds.Offset(0, frame_view->NonClientTopBorderHeight());
- }
- return frame_view->GetWindowBoundsForClientBounds(visible_bounds);
- }
- return GetVisibleBounds();
- }
- gfx::Point ClientControlledShellSurface::GetSurfaceOrigin() const {
- return gfx::Point();
- }
- bool ClientControlledShellSurface::OnPreWidgetCommit() {
- if (!widget_) {
- // Modify the |origin_| to the |pending_geometry_| to place the window on
- // the intended display. See b/77472684 for details.
- // TODO(domlaskowski): Remove this once clients migrate to geometry API with
- // explicit target display.
- if (!pending_geometry_.IsEmpty())
- origin_ = pending_geometry_.origin();
- CreateShellSurfaceWidget(
- chromeos::ToWindowShowState(pending_window_state_));
- }
- // Finish ignoring obsolete bounds update as the state changes caused by
- // display rotation are synced.
- // TODO(takise): This assumes no other bounds update happens during screen
- // rotation. Implement more robust logic to handle synchronization for
- // screen rotation.
- if (pending_geometry_ != geometry_)
- display_rotating_with_pip_ = false;
- ash::WindowState* window_state = GetWindowState();
- state_changed_ = window_state->GetStateType() != pending_window_state_;
- if (!state_changed_) {
- // Animate PIP window movement unless it is being dragged.
- client_controlled_state_->set_next_bounds_change_animation_type(
- window_state->IsPip() && !window_state->is_dragged()
- ? ash::WindowState::BoundsChangeAnimationType::kAnimate
- : ash::WindowState::BoundsChangeAnimationType::kNone);
- return true;
- }
- if (IsPinned(window_state)) {
- VLOG(1) << "State change was requested while pinned";
- return true;
- }
- auto animation_type = ash::WindowState::BoundsChangeAnimationType::kNone;
- switch (pending_window_state_) {
- case chromeos::WindowStateType::kNormal:
- if (widget_->IsMaximized() || widget_->IsFullscreen()) {
- animation_type =
- ash::WindowState::BoundsChangeAnimationType::kCrossFade;
- }
- break;
- case chromeos::WindowStateType::kMaximized:
- case chromeos::WindowStateType::kFullscreen:
- if (!window_state->IsPip())
- animation_type =
- ash::WindowState::BoundsChangeAnimationType::kCrossFade;
- break;
- default:
- break;
- }
- bool wasPip = window_state->IsPip();
- // As the bounds of the widget is updated later, ensure that no bounds change
- // happens with this state change (e.g. updatePipBounds can be triggered).
- base::AutoReset<bool> resetter(&ignore_bounds_change_request_, true);
- if (client_controlled_state_->EnterNextState(window_state,
- pending_window_state_)) {
- client_controlled_state_->set_next_bounds_change_animation_type(
- animation_type);
- UpdateCornerRadius();
- }
- if (wasPip && !window_state->IsMinimized()) {
- // Expanding PIP should end tablet split view (see crbug.com/941788).
- // Clamshell split view does not require special handling. We activate the
- // PIP window, and so overview ends, which means clamshell split view ends.
- // TODO(edcourtney): Consider not ending tablet split view on PIP expand.
- // See crbug.com/950827.
- ash::SplitViewController* split_view_controller =
- ash::SplitViewController::Get(ash::Shell::GetPrimaryRootWindow());
- if (split_view_controller->InTabletSplitViewMode())
- split_view_controller->EndSplitView();
- // As Android doesn't activate PIP tasks after they are expanded, we need
- // to do it here explicitly.
- // TODO(937738): Investigate if we can activate PIP windows inside commit.
- window_state->Activate();
- }
- return true;
- }
- void ClientControlledShellSurface::OnPostWidgetCommit() {
- DCHECK(widget_);
- UpdateFrame();
- UpdateBackdrop();
- if (delegate_) {
- // Since the visible bounds are in screen coordinates, do not scale these
- // bounds with the display's scale before sending them.
- // TODO(b/167286795): Instead of sending bounds in screen coordinates, send
- // the bounds in the display along with the display information, similar to
- // the bounds_changed_callback_.
- delegate_->OnGeometryChanged(GetVisibleBounds());
- }
- // Apply new top inset height.
- if (pending_top_inset_height_ != top_inset_height_) {
- widget_->GetNativeWindow()->SetProperty(aura::client::kTopViewInset,
- pending_top_inset_height_);
- top_inset_height_ = pending_top_inset_height_;
- }
- // Update surface scale.
- if (use_default_scale_cancellation_)
- CommitPendingScale();
- widget_->GetNativeWindow()->SetProperty(aura::client::kZOrderingKey,
- pending_always_on_top_
- ? ui::ZOrderLevel::kFloatingWindow
- : ui::ZOrderLevel::kNormal);
- UpdateResizability();
- ash::WindowState* window_state = GetWindowState();
- // For PIP, the snap fraction is used to specify the ideal position. Usually
- // this value is set in CompleteDrag, but for the initial position, we need
- // to set it here, when the transition is completed.
- if (window_state->IsPip() &&
- !ash::PipPositioner::HasSnapFraction(window_state)) {
- ash::PipPositioner::SaveSnapFraction(
- window_state, window_state->window()->GetBoundsInScreen());
- }
- ShellSurfaceBase::OnPostWidgetCommit();
- }
- void ClientControlledShellSurface::OnSurfaceDestroying(Surface* surface) {
- if (client_controlled_state_)
- client_controlled_state_->ResetDelegate();
- ShellSurfaceBase::OnSurfaceDestroying(surface);
- }
- void ClientControlledShellSurface::OnContentSizeChanged(Surface* surface) {
- CommitPendingScale();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ClientControlledShellSurface, private:
- void ClientControlledShellSurface::UpdateFrame() {
- if (!widget_)
- return;
- ash::WindowState* window_state = GetWindowState();
- bool enable_wide_frame = false;
- if (GetFrameView()->GetFrameEnabled() &&
- window_state->IsMaximizedOrFullscreenOrPinned()) {
- gfx::Rect ideal_bounds =
- GetIdealBoundsForMaximizedOrFullscreenOrPinnedState(
- widget_->GetNativeWindow());
- enable_wide_frame = ideal_bounds.width() != geometry().width();
- }
- bool update_frame = state_changed_;
- state_changed_ = false;
- if (enable_wide_frame) {
- if (!wide_frame_) {
- update_frame = true;
- wide_frame_ = std::make_unique<ash::WideFrameView>(widget_);
- chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, false);
- wide_frame_->Init(immersive_fullscreen_controller_.get());
- wide_frame_->header_view()->GetFrameHeader()->SetFrameTextOverride(
- GetFrameView()
- ->GetHeaderView()
- ->GetFrameHeader()
- ->frame_text_override());
- wide_frame_->GetWidget()->Show();
- // Restoring window targeter replaced by ImmersiveFullscreenController.
- InstallCustomWindowTargeter();
- UpdateCaptionButtonModel();
- }
- DCHECK_EQ(chromeos::FrameHeader::Get(widget_),
- wide_frame_->header_view()->GetFrameHeader());
- } else {
- if (wide_frame_) {
- update_frame = true;
- chromeos::ImmersiveFullscreenController::EnableForWidget(widget_, false);
- wide_frame_.reset();
- GetFrameView()->InitImmersiveFullscreenControllerForView(
- immersive_fullscreen_controller_.get());
- // Restoring window targeter replaced by ImmersiveFullscreenController.
- InstallCustomWindowTargeter();
- UpdateCaptionButtonModel();
- }
- DCHECK_EQ(chromeos::FrameHeader::Get(widget_),
- GetFrameView()->GetHeaderView()->GetFrameHeader());
- UpdateFrameWidth();
- }
- // The autohide should be applied when the window state is in
- // maximzied, fullscreen or pinned. Update the auto hide state
- // inside commit.
- if (update_frame)
- UpdateAutoHideFrame();
- }
- void ClientControlledShellSurface::UpdateCaptionButtonModel() {
- auto model = std::make_unique<CaptionButtonModel>(frame_visible_button_mask_,
- frame_enabled_button_mask_);
- if (wide_frame_)
- wide_frame_->SetCaptionButtonModel(std::move(model));
- else
- GetFrameView()->SetCaptionButtonModel(std::move(model));
- }
- void ClientControlledShellSurface::UpdateBackdrop() {
- aura::Window* window = widget_->GetNativeWindow();
- // Always create a backdrop regardless of the geometry because
- // maximized/fullscreen widget's geometry can be cropped.
- bool enable_backdrop = widget_->IsFullscreen() || widget_->IsMaximized();
- ash::WindowBackdrop::BackdropMode target_backdrop_mode =
- enable_backdrop ? ash::WindowBackdrop::BackdropMode::kEnabled
- : ash::WindowBackdrop::BackdropMode::kAuto;
- ash::WindowBackdrop* window_backdrop = ash::WindowBackdrop::Get(window);
- if (window_backdrop->mode() != target_backdrop_mode)
- window_backdrop->SetBackdropMode(target_backdrop_mode);
- }
- void ClientControlledShellSurface::UpdateFrameWidth() {
- int width = -1;
- if (shadow_bounds_) {
- float device_scale_factor =
- GetWidget()->GetNativeWindow()->layer()->device_scale_factor();
- float dsf_to_default_dsf = device_scale_factor / scale_;
- width = base::ClampRound(shadow_bounds_->width() * dsf_to_default_dsf);
- }
- static_cast<ash::HeaderView*>(GetFrameView()->GetHeaderView())
- ->SetWidthInPixels(width);
- }
- void ClientControlledShellSurface::UpdateFrameType() {
- if (container_ == ash::kShellWindowId_SystemModalContainer &&
- pending_frame_type_ != SurfaceFrameType::NONE) {
- LOG(WARNING)
- << "A surface in system modal container should not have a frame:"
- << static_cast<int>(pending_frame_type_);
- return;
- }
- // TODO(oshima): We shouldn't send the synthesized motion event when just
- // changing the frame type. The better solution would be to keep the window
- // position regardless of the frame state, but that won't be available until
- // next arc version.
- // This is a stopgap solution not to generate the event until it is resolved.
- EventTargetingBlocker blocker;
- bool suppress_mouse_event = frame_type_ != pending_frame_type_ && widget_;
- if (suppress_mouse_event)
- blocker.Block(widget_->GetNativeWindow());
- ShellSurfaceBase::OnSetFrame(pending_frame_type_);
- UpdateAutoHideFrame();
- if (suppress_mouse_event)
- UpdateSurfaceBounds();
- }
- bool ClientControlledShellSurface::GetCanResizeFromSizeConstraints() const {
- // Both min and max bounds of unresizable, maximized ARC windows are empty
- // because Ash requires maximizable apps have empty max bounds.
- // This assumes that ARC sets non-empty min sizes to all resizable apps.
- //
- // Example values of size constraints:
- // ----------------------------------------------------------------------
- // | | resizable | non-resizable |
- // ----------------------------------------------------------------------
- // | freeform | min: (400, 400), max: (0, 0) | min = max = window size |
- // ----------------------------------------------------------------------
- // | maximized | min: (400, 400), max: (0, 0) | min = max = (0, 0) |
- // ----------------------------------------------------------------------
- return minimum_size_ != maximum_size_;
- }
- void ClientControlledShellSurface::
- EnsureCompositorIsLockedForOrientationChange() {
- if (!orientation_compositor_lock_) {
- ui::Compositor* compositor =
- widget_->GetNativeWindow()->layer()->GetCompositor();
- orientation_compositor_lock_ = compositor->GetCompositorLock(
- this, base::Milliseconds(kOrientationLockTimeoutMs));
- }
- }
- ash::WindowState* ClientControlledShellSurface::GetWindowState() {
- return ash::WindowState::Get(widget_->GetNativeWindow());
- }
- ash::NonClientFrameViewAsh* ClientControlledShellSurface::GetFrameView() {
- return static_cast<ash::NonClientFrameViewAsh*>(
- widget_->non_client_view()->frame_view());
- }
- const ash::NonClientFrameViewAsh* ClientControlledShellSurface::GetFrameView()
- const {
- return static_cast<const ash::NonClientFrameViewAsh*>(
- widget_->non_client_view()->frame_view());
- }
- void ClientControlledShellSurface::EnsurePendingScale(bool commit_immediately) {
- // Handle the case where we receive bounds from the client before the initial
- // scale has been set or |pending_scale_| is stale due to change of displays.
- if (pending_scale_ == 0.0) {
- DCHECK(!use_default_scale_cancellation_);
- display::Display display;
- if (display::Screen::GetScreen()->GetDisplayWithDisplayId(
- pending_display_id_, &display)) {
- SetScale(display.device_scale_factor());
- if (commit_immediately)
- CommitPendingScale();
- }
- }
- }
- float ClientControlledShellSurface::GetClientToDpPendingScale() const {
- // When the client is scale-aware, we expect that it will resize windows when
- // reacting to scale changes. Since we do not commit the scale until the
- // buffer size changes, any bounds sent after a scale change and before the
- // scale commit will result in mismatched sizes between widget and the buffer.
- // To work around this, we use pending_scale_ to calculate bounds in DP
- // instead of GetClientToDpScale().
- return use_default_scale_cancellation_ ? 1.f : 1.f / pending_scale_;
- }
- gfx::Rect
- ClientControlledShellSurface::GetClientBoundsForWindowBoundsAndWindowState(
- const gfx::Rect& window_bounds,
- chromeos::WindowStateType window_state) const {
- // The client's geometry uses fullscreen in client controlled,
- // (but the surface is placed under the frame), so just use
- // the window bounds instead for maximixed state.
- // Snapped window states in tablet mode do not include the caption height.
- const bool is_snapped =
- window_state == chromeos::WindowStateType::kPrimarySnapped ||
- window_state == chromeos::WindowStateType::kSecondarySnapped;
- const bool is_maximized =
- window_state == chromeos::WindowStateType::kMaximized;
- const display::TabletState tablet_state =
- chromeos::TabletState::Get()->state();
- const bool is_tablet_mode = WMHelper::GetInstance()->InTabletMode();
- if (is_maximized || (is_snapped && is_tablet_mode))
- return window_bounds;
- gfx::Rect client_bounds =
- GetFrameView()->GetClientBoundsForWindowBounds(window_bounds);
- if (is_snapped && tablet_state == display::TabletState::kExitingTabletMode) {
- // Until the next commit, the frame view is in immersive mode, and the above
- // GetClientBoundsForWindowBounds doesn't return bounds taking the caption
- // height into account.
- client_bounds.Inset(gfx::Insets().set_top(
- GetFrameView()->NonClientTopBorderPreferredHeight()));
- }
- return client_bounds;
- }
- // static
- void ClientControlledShellSurface::
- SetClientControlledStateDelegateFactoryForTest(
- const DelegateFactoryCallback& callback) {
- auto& factory = GetFactoryForTesting();
- factory = callback;
- }
- } // namespace exo
|