1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840 |
- // Copyright 2015 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/shell_surface_base.h"
- #include <algorithm>
- #include "ash/constants/ash_constants.h"
- #include "ash/constants/ash_features.h"
- #include "ash/display/screen_orientation_controller.h"
- #include "ash/frame/non_client_frame_view_ash.h"
- #include "ash/public/cpp/ash_constants.h"
- #include "ash/public/cpp/rounded_corner_utils.h"
- #include "ash/public/cpp/shelf_types.h"
- #include "ash/public/cpp/shell_window_ids.h"
- #include "ash/public/cpp/window_properties.h"
- #include "ash/screen_util.h"
- #include "ash/shell.h"
- #include "ash/wm/desks/desks_controller.h"
- #include "ash/wm/desks/desks_util.h"
- #include "ash/wm/drag_window_resizer.h"
- #include "ash/wm/resize_shadow_controller.h"
- #include "ash/wm/screen_pinning_controller.h"
- #include "ash/wm/window_resizer.h"
- #include "ash/wm/window_state.h"
- #include "ash/wm/window_util.h"
- #include "base/check.h"
- #include "base/debug/stack_trace.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/stringprintf.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/trace_event/trace_event.h"
- #include "base/trace_event/traced_value.h"
- #include "build/chromeos_buildflags.h"
- #include "cc/trees/layer_tree_frame_sink.h"
- #include "chromeos/crosapi/cpp/crosapi_constants.h"
- #include "chromeos/ui/base/chromeos_ui_constants.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/snap_controller.h"
- #include "chromeos/ui/frame/multitask_menu/float_controller_base.h"
- #include "components/app_restore/app_restore_info.h"
- #include "components/app_restore/app_restore_utils.h"
- #include "components/app_restore/window_properties.h"
- #include "components/exo/custom_window_state_delegate.h"
- #include "components/exo/security_delegate.h"
- #include "components/exo/shell_surface_util.h"
- #include "components/exo/surface.h"
- #include "components/exo/window_properties.h"
- #include "components/exo/wm_helper.h"
- #include "third_party/skia/include/core/SkPath.h"
- #include "ui/accessibility/ax_enums.mojom.h"
- #include "ui/accessibility/ax_node_data.h"
- #include "ui/aura/client/aura_constants.h"
- #include "ui/aura/client/capture_client.h"
- #include "ui/aura/client/cursor_client.h"
- #include "ui/aura/client/focus_client.h"
- #include "ui/aura/window.h"
- #include "ui/aura/window_observer.h"
- #include "ui/aura/window_occlusion_tracker.h"
- #include "ui/aura/window_targeter.h"
- #include "ui/aura/window_tree_host.h"
- #include "ui/base/accelerators/accelerator.h"
- #include "ui/base/class_property.h"
- #include "ui/base/ui_base_features.h"
- #include "ui/compositor/compositor.h"
- #include "ui/compositor/layer.h"
- #include "ui/compositor_extra/shadow.h"
- #include "ui/display/display.h"
- #include "ui/display/screen.h"
- #include "ui/gfx/geometry/size_conversions.h"
- #include "ui/gfx/geometry/vector2d_conversions.h"
- #include "ui/views/accessibility/view_accessibility.h"
- #include "ui/views/layout/fill_layout.h"
- #include "ui/views/widget/widget.h"
- #include "ui/wm/core/coordinate_conversion.h"
- #include "ui/wm/core/shadow_controller.h"
- #include "ui/wm/core/shadow_types.h"
- #include "ui/wm/core/window_animations.h"
- #include "ui/wm/core/window_util.h"
- namespace exo {
- namespace {
- // The accelerator keys used to close ShellSurfaces.
- const struct {
- ui::KeyboardCode keycode;
- int modifiers;
- } kCloseWindowAccelerators[] = {
- {ui::VKEY_W, ui::EF_CONTROL_DOWN},
- {ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN},
- {ui::VKEY_F4, ui::EF_ALT_DOWN}};
- class ShellSurfaceWidget : public views::Widget {
- public:
- ShellSurfaceWidget() = default;
- ShellSurfaceWidget(const ShellSurfaceWidget&) = delete;
- ShellSurfaceWidget& operator=(const ShellSurfaceWidget&) = delete;
- // Overridden from views::Widget:
- void OnKeyEvent(ui::KeyEvent* event) override {
- if (GetFocusManager()->GetFocusedView() &&
- GetFocusManager()->GetFocusedView()->GetWidget() != this) {
- // If the focus is on the overlay widget, dispatch the key event normally.
- views::Widget::OnKeyEvent(event);
- } else if (GetFocusManager()->ProcessAccelerator(ui::Accelerator(*event))) {
- // Otherwise handle only accelerators. Do not call Widget::OnKeyEvent that
- // eats focus management keys (like the tab key) as well.
- event->SetHandled();
- }
- }
- };
- class CustomFrameView : public ash::NonClientFrameViewAsh {
- public:
- using ShapeRects = std::vector<gfx::Rect>;
- CustomFrameView(views::Widget* widget,
- ShellSurfaceBase* shell_surface,
- bool enabled)
- : NonClientFrameViewAsh(widget), shell_surface_(shell_surface) {
- SetFrameEnabled(enabled);
- if (!enabled)
- NonClientFrameViewAsh::SetShouldPaintHeader(false);
- }
- CustomFrameView(const CustomFrameView&) = delete;
- CustomFrameView& operator=(const CustomFrameView&) = delete;
- ~CustomFrameView() override = default;
- // Overridden from ash::NonClientFrameViewAsh:
- void SetShouldPaintHeader(bool paint) override {
- if (GetFrameEnabled()) {
- NonClientFrameViewAsh::SetShouldPaintHeader(paint);
- return;
- }
- }
- // Overridden from views::NonClientFrameView:
- gfx::Rect GetBoundsForClientView() const override {
- if (GetFrameEnabled())
- return ash::NonClientFrameViewAsh::GetBoundsForClientView();
- return bounds();
- }
- gfx::Rect GetWindowBoundsForClientBounds(
- const gfx::Rect& client_bounds) const override {
- if (GetFrameEnabled()) {
- return ash::NonClientFrameViewAsh::GetWindowBoundsForClientBounds(
- client_bounds);
- }
- return client_bounds;
- }
- int NonClientHitTest(const gfx::Point& point) override {
- if (GetFrameEnabled() || shell_surface_->server_side_resize())
- return ash::NonClientFrameViewAsh::NonClientHitTest(point);
- return GetWidget()->client_view()->NonClientHitTest(point);
- }
- void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override {
- if (GetFrameEnabled())
- return ash::NonClientFrameViewAsh::GetWindowMask(size, window_mask);
- }
- void ResetWindowControls() override {
- if (GetFrameEnabled())
- return ash::NonClientFrameViewAsh::ResetWindowControls();
- }
- void UpdateWindowIcon() override {
- if (GetFrameEnabled())
- return ash::NonClientFrameViewAsh::ResetWindowControls();
- }
- void UpdateWindowTitle() override {
- if (GetFrameEnabled())
- return ash::NonClientFrameViewAsh::UpdateWindowTitle();
- }
- void SizeConstraintsChanged() override {
- if (GetFrameEnabled())
- return ash::NonClientFrameViewAsh::SizeConstraintsChanged();
- }
- gfx::Size GetMinimumSize() const override {
- gfx::Size minimum_size = shell_surface_->GetMinimumSize();
- if (GetFrameEnabled()) {
- return ash::NonClientFrameViewAsh::GetWindowBoundsForClientBounds(
- gfx::Rect(minimum_size))
- .size();
- }
- return minimum_size;
- }
- gfx::Size GetMaximumSize() const override {
- gfx::Size maximum_size = shell_surface_->GetMaximumSize();
- if (GetFrameEnabled() && !maximum_size.IsEmpty()) {
- return ash::NonClientFrameViewAsh::GetWindowBoundsForClientBounds(
- gfx::Rect(maximum_size))
- .size();
- }
- return maximum_size;
- }
- private:
- ShellSurfaceBase* const shell_surface_;
- };
- class CustomWindowTargeter : public aura::WindowTargeter {
- public:
- explicit CustomWindowTargeter(ShellSurfaceBase* shell_surface)
- : shell_surface_(shell_surface), widget_(shell_surface->GetWidget()) {}
- CustomWindowTargeter(const CustomWindowTargeter&) = delete;
- CustomWindowTargeter& operator=(const CustomWindowTargeter&) = delete;
- ~CustomWindowTargeter() override = default;
- // Overridden from aura::WindowTargeter:
- bool EventLocationInsideBounds(aura::Window* window,
- const ui::LocatedEvent& event) const override {
- gfx::Point local_point =
- ConvertEventLocationToWindowCoordinates(window, event);
- if (IsInResizeHandle(window, event, local_point))
- return true;
- Surface* surface = GetShellRootSurface(window);
- if (!surface)
- return false;
- int component =
- widget_->non_client_view()
- ? widget_->non_client_view()->NonClientHitTest(local_point)
- : HTNOWHERE;
- if (component != HTNOWHERE && component != HTCLIENT &&
- component != HTBORDER) {
- return true;
- }
- aura::Window::ConvertPointToTarget(window, surface->window(), &local_point);
- return surface->HitTest(local_point);
- }
- private:
- bool IsInResizeHandle(aura::Window* window,
- const ui::LocatedEvent& event,
- const gfx::Point& local_point) const {
- if (window != widget_->GetNativeWindow() ||
- !widget_->widget_delegate()->CanResize()) {
- return false;
- }
- if (!shell_surface_->server_side_resize())
- return false;
- ui::EventTarget* parent =
- static_cast<ui::EventTarget*>(window)->GetParentTarget();
- if (parent) {
- aura::WindowTargeter* parent_targeter =
- static_cast<aura::WindowTargeter*>(parent->GetEventTargeter());
- if (parent_targeter) {
- gfx::Rect mouse_rect;
- gfx::Rect touch_rect;
- if (parent_targeter->GetHitTestRects(window, &mouse_rect,
- &touch_rect)) {
- const gfx::Vector2d offset = -window->bounds().OffsetFromOrigin();
- mouse_rect.Offset(offset);
- touch_rect.Offset(offset);
- if (event.IsTouchEvent() || event.IsGestureEvent()
- ? touch_rect.Contains(local_point)
- : mouse_rect.Contains(local_point)) {
- return true;
- }
- }
- }
- }
- return false;
- }
- ShellSurfaceBase* shell_surface_;
- views::Widget* const widget_;
- };
- void CloseAllShellSurfaceTransientChildren(aura::Window* window) {
- // Deleting a window may delete other transient children. Remove other shell
- // surface bases first so they don't get deleted.
- auto list = wm::GetTransientChildren(window);
- for (size_t i = 0; i < list.size(); ++i) {
- if (GetShellSurfaceBaseForWindow(list[i]))
- wm::RemoveTransientChild(window, list[i]);
- }
- }
- int shell_id = 0;
- void ShowSnapPreview(aura::Window* window,
- chromeos::SnapDirection snap_direction) {
- chromeos::SnapController::Get()->ShowSnapPreview(
- window, snap_direction,
- /*allow_haptic_feedback=*/false);
- }
- void CommitSnap(aura::Window* window, chromeos::SnapDirection snap_direction) {
- chromeos::SnapController::Get()->CommitSnap(window, snap_direction);
- }
- } // namespace
- ////////////////////////////////////////////////////////////////////////////////
- // ShellSurfaceBase, public:
- ShellSurfaceBase::ShellSurfaceBase(Surface* surface,
- const gfx::Point& origin,
- bool can_minimize,
- int container)
- : SurfaceTreeHost(base::StringPrintf("ExoShellSurfaceHost-%d", shell_id)),
- origin_(origin),
- container_(container),
- can_minimize_(can_minimize) {
- WMHelper::GetInstance()->AddActivationObserver(this);
- surface->AddSurfaceObserver(this);
- SetRootSurface(surface);
- host_window()->Show();
- set_owned_by_client();
- SetCanMinimize(can_minimize_);
- SetCanMaximize(ash::desks_util::IsDeskContainerId(container_));
- SetCanResize(true);
- SetShowTitle(false);
- }
- ShellSurfaceBase::~ShellSurfaceBase() {
- // If the surface was TrustedPinned, we have to unpin first as this might have
- // locked down some system functions.
- if (current_pinned_state_ == chromeos::WindowPinType::kTrustedPinned) {
- pending_pinned_state_ = chromeos::WindowPinType::kNone;
- UpdatePinned();
- }
- // Close the overlay in case the window is deleted by the server.
- overlay_widget_.reset();
- // Remove activation observer before hiding widget to prevent it from
- // casuing the configure callback to be called.
- WMHelper::GetInstance()->RemoveActivationObserver(this);
- // Client is gone by now, so don't call callbacks.
- close_callback_.Reset();
- pre_close_callback_.Reset();
- surface_destroyed_callback_.Reset();
- if (widget_) {
- widget_->GetNativeWindow()->RemoveObserver(this);
- widget_->RemoveObserver(this);
- // Remove transient children which are shell surfaces so they are not
- // automatically destroyed.
- CloseAllShellSurfaceTransientChildren(widget_->GetNativeWindow());
- if (widget_->IsVisible())
- widget_->Hide();
- widget_->CloseNow();
- }
- if (parent_)
- parent_->RemoveObserver(this);
- if (root_surface())
- root_surface()->RemoveSurfaceObserver(this);
- if (has_grab_)
- WMHelper::GetInstance()->GetCaptureClient()->RemoveObserver(this);
- CHECK(!views::WidgetObserver::IsInObserverList());
- }
- void ShellSurfaceBase::Activate() {
- TRACE_EVENT0("exo", "ShellSurfaceBase::Activate");
- if (!widget_ || widget_->IsActive())
- return;
- widget_->Activate();
- }
- void ShellSurfaceBase::SetTitle(const std::u16string& title) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetTitle", "title",
- base::UTF16ToUTF8(title));
- WidgetDelegate::SetTitle(title);
- }
- void ShellSurfaceBase::SetIcon(const gfx::ImageSkia& icon) {
- TRACE_EVENT0("exo", "ShellSurfaceBase::SetIcon");
- WidgetDelegate::SetIcon(icon);
- }
- void ShellSurfaceBase::SetSystemModal(bool system_modal) {
- if (system_modal == system_modal_)
- return;
- bool non_system_modal_window_was_active =
- !system_modal_ && widget_ && widget_->IsActive();
- system_modal_ = system_modal;
- if (widget_) {
- UpdateSystemModal();
- // Deactivate to give the focus back to normal windows.
- if (!system_modal_ && !non_system_modal_window_was_active_) {
- widget_->Deactivate();
- }
- }
- non_system_modal_window_was_active_ = non_system_modal_window_was_active;
- }
- void ShellSurfaceBase::SetBoundsForShadows(
- const absl::optional<gfx::Rect>& shadow_bounds) {
- if (shadow_bounds_ != shadow_bounds) {
- // Set normal shadow bounds.
- shadow_bounds_ = shadow_bounds;
- shadow_bounds_changed_ = true;
- if (widget_ && shadow_bounds) {
- // Set resize shadow bounds and origin.
- const gfx::Rect bounds = shadow_bounds.value();
- const gfx::Point absolute_origin =
- widget_->GetNativeWindow()->bounds().origin();
- const gfx::Rect absolute_bounds =
- gfx::Rect(absolute_origin.x(), absolute_origin.y(), bounds.width(),
- bounds.height());
- ash::Shell::Get()
- ->resize_shadow_controller()
- ->UpdateResizeShadowBoundsOfWindow(widget_->GetNativeWindow(),
- absolute_bounds);
- }
- }
- }
- void ShellSurfaceBase::UpdateSystemModal() {
- DCHECK(widget_);
- DCHECK_EQ(container_, ash::kShellWindowId_SystemModalContainer);
- widget_->GetNativeWindow()->SetProperty(
- aura::client::kModalKey,
- system_modal_ ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE);
- }
- void ShellSurfaceBase::SetApplicationId(const char* application_id) {
- // Store the value in |application_id_| in case the window does not exist yet.
- if (application_id)
- application_id_ = std::string(application_id);
- else
- application_id_.reset();
- if (widget_ && widget_->GetNativeWindow()) {
- SetShellApplicationId(widget_->GetNativeWindow(), application_id_);
- WMHelper::AppPropertyResolver::Params params;
- if (application_id_)
- params.app_id = *application_id_;
- if (startup_id_)
- params.startup_id = *startup_id_;
- ui::PropertyHandler& property_handler = *widget_->GetNativeWindow();
- WMHelper::GetInstance()->PopulateAppProperties(params, property_handler);
- }
- this->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
- /* send_native_event */ false);
- }
- void ShellSurfaceBase::SetStartupId(const char* startup_id) {
- // Store the value in |startup_id_| in case the window does not exist yet.
- if (startup_id)
- startup_id_ = std::string(startup_id);
- else
- startup_id_.reset();
- if (widget_ && widget_->GetNativeWindow())
- SetShellStartupId(widget_->GetNativeWindow(), startup_id_);
- }
- void ShellSurfaceBase::SetUseImmersiveForFullscreen(bool value) {
- // Store the value in case the window doesn't exist yet.
- immersive_implied_by_fullscreen_ = value;
- if (widget_ && widget_->GetNativeWindow())
- SetShellUseImmersiveForFullscreen(widget_->GetNativeWindow(), value);
- }
- void ShellSurfaceBase::ShowSnapPreviewToPrimary() {
- ShowSnapPreview(widget_->GetNativeWindow(),
- chromeos::SnapDirection::kPrimary);
- }
- void ShellSurfaceBase::ShowSnapPreviewToSecondary() {
- ShowSnapPreview(widget_->GetNativeWindow(),
- chromeos::SnapDirection::kSecondary);
- }
- void ShellSurfaceBase::HideSnapPreview() {
- ShowSnapPreview(widget_->GetNativeWindow(), chromeos::SnapDirection::kNone);
- }
- void ShellSurfaceBase::SetSnappedToPrimary() {
- CommitSnap(widget_->GetNativeWindow(), chromeos::SnapDirection::kPrimary);
- }
- void ShellSurfaceBase::SetSnappedToSecondary() {
- CommitSnap(widget_->GetNativeWindow(), chromeos::SnapDirection::kSecondary);
- }
- void ShellSurfaceBase::UnsetSnap() {
- CommitSnap(widget_->GetNativeWindow(), chromeos::SnapDirection::kNone);
- }
- void ShellSurfaceBase::SetCanGoBack() {
- if (widget_)
- widget_->GetNativeWindow()->SetProperty(ash::kMinimizeOnBackKey, false);
- }
- void ShellSurfaceBase::UnsetCanGoBack() {
- if (widget_)
- widget_->GetNativeWindow()->SetProperty(ash::kMinimizeOnBackKey, true);
- }
- void ShellSurfaceBase::SetPip() {
- if (!widget_) {
- pending_pip_ = true;
- return;
- }
- pending_pip_ = false;
- // Set all the necessary window properties and window state.
- auto* window = widget_->GetNativeWindow();
- window->SetProperty(ash::kWindowPipTypeKey, true);
- window->SetProperty(aura::client::kZOrderingKey,
- ui::ZOrderLevel::kFloatingWindow);
- if (initial_bounds_)
- return;
- // If no initial bounds is specified, pip windows should start in the bottom
- // right corner of the screen so move |window| to the bottom right of the
- // work area and let the pip positioner move it within the work area.
- auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(window);
- gfx::Size window_size = window->bounds().size();
- window->SetBoundsInScreen(
- gfx::Rect(display.work_area().bottom_right(), window_size), display);
- }
- void ShellSurfaceBase::UnsetPip() {
- if (!widget_) {
- pending_pip_ = false;
- return;
- }
- // Set all the necessary window properties and window state.
- auto* window = widget_->GetNativeWindow();
- window->SetProperty(ash::kWindowPipTypeKey, false);
- window->SetProperty(aura::client::kZOrderingKey, ui::ZOrderLevel::kNormal);
- }
- void ShellSurfaceBase::MoveToDesk(int desk_index) {
- if (widget_) {
- ash::DesksController::Get()->SendToDeskAtIndex(widget_->GetNativeWindow(),
- desk_index);
- }
- }
- void ShellSurfaceBase::SetVisibleOnAllWorkspaces() {
- if (widget_)
- widget_->SetVisibleOnAllWorkspaces(true);
- }
- void ShellSurfaceBase::SetInitialWorkspace(const char* initial_workspace) {
- if (initial_workspace)
- initial_workspace_ = std::string(initial_workspace);
- else
- initial_workspace_.reset();
- }
- void ShellSurfaceBase::Pin(bool trusted) {
- pending_pinned_state_ = trusted ? chromeos::WindowPinType::kTrustedPinned
- : chromeos::WindowPinType::kPinned;
- UpdatePinned();
- }
- void ShellSurfaceBase::Unpin() {
- // Only need to do something when we have to set a pinned mode.
- if (pending_pinned_state_ == chromeos::WindowPinType::kNone)
- return;
- // Remove any pending pin states which might not have been applied yet.
- pending_pinned_state_ = chromeos::WindowPinType::kNone;
- UpdatePinned();
- }
- void ShellSurfaceBase::UpdatePinned() {
- if (!widget_) {
- // It is possible to get here before the widget has actually been created.
- // The state will be set once the widget gets created.
- return;
- }
- if (current_pinned_state_ != pending_pinned_state_) {
- auto* window = widget_->GetNativeWindow();
- if (pending_pinned_state_ == chromeos::WindowPinType::kNone) {
- ash::WindowState::Get(window)->Restore();
- } else {
- bool trusted_pinned =
- pending_pinned_state_ == chromeos::WindowPinType::kTrustedPinned;
- ash::window_util::PinWindow(window,
- /*trusted=*/trusted_pinned);
- }
- current_pinned_state_ = pending_pinned_state_;
- }
- }
- void ShellSurfaceBase::SetChildAxTreeId(ui::AXTreeID child_ax_tree_id) {
- GetViewAccessibility().OverrideChildTreeID(child_ax_tree_id);
- this->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false);
- }
- void ShellSurfaceBase::SetGeometry(const gfx::Rect& geometry) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetGeometry", "geometry",
- geometry.ToString());
- if (geometry.IsEmpty()) {
- DLOG(WARNING) << "Surface geometry must be non-empty";
- return;
- }
- pending_geometry_ = geometry;
- }
- void ShellSurfaceBase::SetWindowBounds(const gfx::Rect& bounds) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetWindowBounds", "bounds",
- bounds.ToString());
- if (!widget_) {
- initial_bounds_.emplace(bounds);
- return;
- }
- // TODO(crbug.com/1261321): This will not work if the full server side
- // decoration is used.
- DCHECK(!frame_enabled());
- widget_->SetBounds(bounds);
- }
- void ShellSurfaceBase::SetRestoreInfo(int32_t restore_session_id,
- int32_t restore_window_id) {
- // TODO(crbug.com/1327490): Rename restore info variables.
- // Restore information must be set before widget is created.
- DCHECK(!widget_);
- restore_session_id_.emplace(restore_session_id);
- restore_window_id_.emplace(restore_window_id);
- }
- void ShellSurfaceBase::SetRestoreInfoWithWindowIdSource(
- int32_t restore_session_id,
- const std::string& restore_window_id_source) {
- restore_session_id_.emplace(restore_session_id);
- if (!restore_window_id_source.empty())
- restore_window_id_source_.emplace(restore_window_id_source);
- }
- void ShellSurfaceBase::SetFloat() {
- // TODO(crbug.com/1347534): This currently can unset float as well, but its
- // necessary until configure request is ready otherwise the window will remain
- // floated forever.
- chromeos::FloatControllerBase::Get()->ToggleFloat(widget_->GetNativeWindow());
- }
- void ShellSurfaceBase::UnsetFloat() {
- chromeos::FloatControllerBase::Get()->ToggleFloat(widget_->GetNativeWindow());
- }
- void ShellSurfaceBase::SetDisplay(int64_t display_id) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetDisplay", "display_id", display_id);
- pending_display_id_ = display_id;
- }
- void ShellSurfaceBase::SetOrigin(const gfx::Point& origin) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetOrigin", "origin",
- origin.ToString());
- origin_ = origin;
- }
- void ShellSurfaceBase::SetActivatable(bool activatable) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetActivatable", "activatable",
- activatable);
- activatable_ = activatable;
- }
- void ShellSurfaceBase::SetContainer(int container) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetContainer", "container", container);
- SetContainerInternal(container);
- }
- void ShellSurfaceBase::SetMaximumSize(const gfx::Size& size) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetMaximumSize", "size",
- size.ToString());
- pending_maximum_size_ = size;
- }
- void ShellSurfaceBase::SetMinimumSize(const gfx::Size& size) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetMinimumSize", "size",
- size.ToString());
- pending_minimum_size_ = size;
- }
- void ShellSurfaceBase::SetAspectRatio(const gfx::SizeF& aspect_ratio) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetAspectRatio", "aspect_ratio",
- aspect_ratio.ToString());
- pending_aspect_ratio_ = aspect_ratio;
- }
- void ShellSurfaceBase::SetCanMinimize(bool can_minimize) {
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetCanMinimize", "can_minimize",
- can_minimize);
- can_minimize_ = can_minimize;
- WidgetDelegate::SetCanMinimize(!parent_ && can_minimize_);
- }
- void ShellSurfaceBase::SetMenu() {
- is_menu_ = true;
- }
- void ShellSurfaceBase::DisableMovement() {
- movement_disabled_ = true;
- SetCanResize(false);
- if (widget_)
- widget_->set_movement_disabled(true);
- }
- void ShellSurfaceBase::UpdateResizability() {
- SetCanResize(CalculateCanResize());
- auto max_size = GetMaximumSize();
- // Allow maximizeing if the max size is bigger than 32k resolution.
- SetCanMaximize(CanResize() && !parent_ &&
- ash::desks_util::IsDeskContainerId(container_) &&
- (max_size.IsEmpty() ||
- (max_size.width() > ash::kAllowMaximizeThreshold &&
- max_size.height() > ash::kAllowMaximizeThreshold)));
- }
- void ShellSurfaceBase::RebindRootSurface(Surface* root_surface,
- bool can_minimize,
- int container) {
- can_minimize_ = can_minimize;
- container_ = container;
- this->root_surface()->RemoveSurfaceObserver(this);
- root_surface->AddSurfaceObserver(this);
- SetRootSurface(root_surface);
- host_window()->Show();
- // Re-apply window properties to the new root surface.
- auto* window = widget_ ? widget_->GetNativeWindow() : nullptr;
- if (window) {
- // Int properties.
- for (auto* const key :
- {aura::client::kSkipImeProcessing, chromeos::kFrameRestoreLookKey,
- ash::kFrameRateThrottleKey}) {
- if (base::Contains(window->GetAllPropertyKeys(), key)) {
- OnWindowPropertyChanged(window, key,
- /*old_value(unused)=*/0);
- }
- }
- // Boolean property.
- if (base::Contains(window->GetAllPropertyKeys(),
- aura::client::kWindowWorkspaceKey)) {
- OnWindowPropertyChanged(window, aura::client::kWindowWorkspaceKey,
- /*old_value(unused)=*/0);
- }
- if (window->HasFocus())
- host_window()->Focus();
- }
- SetCanMinimize(can_minimize_);
- SetCanMaximize(ash::desks_util::IsDeskContainerId(container_));
- SetCanResize(true);
- SetShowTitle(false);
- }
- std::unique_ptr<base::trace_event::TracedValue>
- ShellSurfaceBase::AsTracedValue() const {
- std::unique_ptr<base::trace_event::TracedValue> value(
- new base::trace_event::TracedValue());
- value->SetString("title", base::UTF16ToUTF8(GetWindowTitle()));
- if (GetWidget() && GetWidget()->GetNativeWindow()) {
- const std::string* application_id =
- GetShellApplicationId(GetWidget()->GetNativeWindow());
- if (application_id)
- value->SetString("application_id", *application_id);
- const std::string* startup_id =
- GetShellStartupId(GetWidget()->GetNativeWindow());
- if (startup_id)
- value->SetString("startup_id", *startup_id);
- }
- return value;
- }
- void ShellSurfaceBase::AddOverlay(OverlayParams&& overlay_params) {
- DCHECK(widget_);
- DCHECK(!overlay_widget_);
- overlay_overlaps_frame_ = overlay_params.overlaps_frame;
- overlay_can_resize_ = std::move(overlay_params.can_resize);
- views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
- params.parent = widget_->GetNativeWindow();
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- if (overlay_params.translucent)
- params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
- if (overlay_params.focusable)
- params.activatable = views::Widget::InitParams::Activatable::kYes;
- params.delegate = new views::WidgetDelegate();
- params.delegate->SetOwnedByWidget(true);
- params.delegate->SetContentsView(std::move(overlay_params.contents_view));
- params.name = "Overlay";
- overlay_widget_ = std::make_unique<views::Widget>();
- overlay_widget_->Init(std::move(params));
- overlay_widget_->GetNativeWindow()->SetEventTargeter(
- std::make_unique<aura::WindowTargeter>());
- overlay_widget_->Show();
- // Setup Focus Traversal.
- overlay_widget_->SetFocusTraversableParentView(this);
- overlay_widget_->SetFocusTraversableParent(
- GetWidget()->GetFocusTraversable());
- SetFocusTraversesOut(true);
- skip_ime_processing_ = GetWidget()->GetNativeWindow()->GetProperty(
- aura::client::kSkipImeProcessing);
- if (skip_ime_processing_) {
- GetWidget()->GetNativeWindow()->SetProperty(
- aura::client::kSkipImeProcessing, false);
- }
- UpdateWidgetBounds();
- UpdateResizability();
- }
- void ShellSurfaceBase::RemoveOverlay() {
- overlay_widget_.reset();
- SetFocusTraversesOut(false);
- if (skip_ime_processing_) {
- GetWidget()->GetNativeWindow()->SetProperty(
- aura::client::kSkipImeProcessing, true);
- }
- UpdateResizability();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // SurfaceDelegate overrides:
- void ShellSurfaceBase::OnSurfaceCommit() {
- // Pause occlusion tracking since we will update a bunch of window properties.
- aura::WindowOcclusionTracker::ScopedPause pause_occlusion;
- // SetShadowBounds requires synchronizing shadow bounds with the next frame,
- // so submit the next frame to a new surface and let the host window use the
- // new surface.
- if (shadow_bounds_changed_)
- host_window()->AllocateLocalSurfaceId();
- DCHECK(presentation_callbacks().empty());
- root_surface()->CommitSurfaceHierarchy(false);
- if (!OnPreWidgetCommit())
- return;
- CommitWidget();
- OnPostWidgetCommit();
- SubmitCompositorFrame();
- }
- bool ShellSurfaceBase::IsInputEnabled(Surface*) const {
- return true;
- }
- void ShellSurfaceBase::OnSetFrame(SurfaceFrameType frame_type) {
- if (!IsFrameDecorationSupported(frame_type)) {
- DLOG(WARNING)
- << "popup does not support frame decoration other than NONE/SHADOW.";
- return;
- }
- bool frame_was_disabled = !frame_enabled();
- bool frame_type_changed = frame_type_ != frame_type;
- frame_type_ = frame_type;
- switch (frame_type) {
- case SurfaceFrameType::NONE:
- shadow_bounds_.reset();
- break;
- case SurfaceFrameType::NORMAL:
- case SurfaceFrameType::AUTOHIDE:
- case SurfaceFrameType::OVERLAY:
- case SurfaceFrameType::SHADOW:
- // Initialize the shadow if it didn't exist. Do not reset if
- // the frame type just switched from another enabled type or
- // there is a pending shadow_bounds_ change to avoid overriding
- // a shadow bounds which have been changed and not yet committed.
- if (frame_type_changed &&
- (!shadow_bounds_ || (frame_was_disabled && !shadow_bounds_changed_)))
- shadow_bounds_ = gfx::Rect();
- break;
- }
- if (!widget_)
- return;
- // Override redirect window and popup can request NONE/SHADOW. The shadow
- // will be updated in next commit.
- if (widget_->non_client_view()) {
- CustomFrameView* frame_view =
- static_cast<CustomFrameView*>(widget_->non_client_view()->frame_view());
- if (frame_view->GetFrameEnabled() == frame_enabled())
- return;
- frame_view->SetFrameEnabled(frame_enabled());
- frame_view->SetShouldPaintHeader(frame_enabled());
- }
- widget_->GetRootView()->Layout();
- // TODO(oshima): We probably should wait applying these if the
- // window is animating.
- UpdateWidgetBounds();
- UpdateSurfaceBounds();
- }
- void ShellSurfaceBase::OnSetFrameColors(SkColor active_color,
- SkColor inactive_color) {
- has_frame_colors_ = true;
- active_frame_color_ = SkColorSetA(active_color, SK_AlphaOPAQUE);
- inactive_frame_color_ = SkColorSetA(inactive_color, SK_AlphaOPAQUE);
- if (widget_) {
- // Set kTrackDefaultFrameColors to false to prevent clobbering the active
- // and inactive frame colors during theme changes.
- widget_->GetNativeWindow()->SetProperty(chromeos::kTrackDefaultFrameColors,
- false);
- widget_->GetNativeWindow()->SetProperty(chromeos::kFrameActiveColorKey,
- active_frame_color_);
- widget_->GetNativeWindow()->SetProperty(chromeos::kFrameInactiveColorKey,
- inactive_frame_color_);
- }
- }
- void ShellSurfaceBase::OnSetStartupId(const char* startup_id) {
- SetStartupId(startup_id);
- }
- void ShellSurfaceBase::OnSetApplicationId(const char* application_id) {
- SetApplicationId(application_id);
- }
- void ShellSurfaceBase::OnActivationRequested() {
- if (widget_ && GetSecurityDelegate() &&
- GetSecurityDelegate()->CanSelfActivate(widget_->GetNativeWindow())) {
- this->Activate();
- }
- }
- void ShellSurfaceBase::OnSetServerStartResize() {
- server_side_resize_ = true;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // SurfaceObserver overrides:
- void ShellSurfaceBase::OnSurfaceDestroying(Surface* surface) {
- DCHECK_EQ(root_surface(), surface);
- surface->RemoveSurfaceObserver(this);
- SetRootSurface(nullptr);
- overlay_widget_.reset();
- if (widget_)
- SetShellRootSurface(widget_->GetNativeWindow(), nullptr);
- // Hide widget before surface is destroyed. This allows hide animations to
- // run using the current surface contents.
- if (widget_) {
- // Remove transient children which are shell surfaces so they are not
- // automatically hidden.
- CloseAllShellSurfaceTransientChildren(widget_->GetNativeWindow());
- widget_->Hide();
- }
- // Note: In its use in the Wayland server implementation, the surface
- // destroyed callback may destroy the ShellSurface instance. This call needs
- // to be last so that the instance can be destroyed.
- if (!surface_destroyed_callback_.is_null())
- std::move(surface_destroyed_callback_).Run();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // views::WidgetDelegate overrides:
- bool ShellSurfaceBase::OnCloseRequested(
- views::Widget::ClosedReason close_reason) {
- if (!pre_close_callback_.is_null())
- pre_close_callback_.Run();
- // Closing the shell surface is a potentially asynchronous operation, so we
- // will defer actually closing the Widget right now, and come back and call
- // CloseNow() when the callback completes and the shell surface is destroyed
- // (see ~ShellSurfaceBase()).
- if (!close_callback_.is_null())
- close_callback_.Run();
- return false;
- }
- void ShellSurfaceBase::WindowClosing() {
- SetEnabled(false);
- if (widget_)
- widget_->RemoveObserver(this);
- widget_ = nullptr;
- }
- views::Widget* ShellSurfaceBase::GetWidget() {
- return widget_;
- }
- const views::Widget* ShellSurfaceBase::GetWidget() const {
- return widget_;
- }
- views::View* ShellSurfaceBase::GetContentsView() {
- return this;
- }
- std::unique_ptr<views::NonClientFrameView>
- ShellSurfaceBase::CreateNonClientFrameView(views::Widget* widget) {
- return CreateNonClientFrameViewInternal(widget);
- }
- bool ShellSurfaceBase::ShouldSaveWindowPlacement() const {
- return !is_popup_ && !movement_disabled_;
- }
- bool ShellSurfaceBase::WidgetHasHitTestMask() const {
- return true;
- }
- void ShellSurfaceBase::GetWidgetHitTestMask(SkPath* mask) const {
- GetHitTestMask(mask);
- gfx::Point origin = host_window()->bounds().origin();
- SkMatrix matrix;
- float scale = GetScale();
- matrix.setScaleTranslate(
- SkFloatToScalar(1.0f / scale), SkFloatToScalar(1.0f / scale),
- SkIntToScalar(origin.x()), SkIntToScalar(origin.y()));
- mask->transform(matrix);
- }
- void ShellSurfaceBase::OnCaptureChanged(aura::Window* lost_capture,
- aura::Window* gained_capture) {
- if (lost_capture == widget_->GetNativeWindow() && is_popup_) {
- WMHelper::GetInstance()->GetCaptureClient()->RemoveObserver(this);
- if (gained_capture &&
- lost_capture == wm::GetTransientParent(gained_capture)) {
- // Don't close if the capture has been transferred to the child popup.
- return;
- }
- aura::Window* parent = wm::GetTransientParent(lost_capture);
- if (parent) {
- // The capture needs to be transferred to the parent if it had grab.
- views::Widget* parent_widget =
- views::Widget::GetWidgetForNativeWindow(parent);
- ShellSurfaceBase* parent_shell_surface = static_cast<ShellSurfaceBase*>(
- parent_widget->widget_delegate()->GetContentsView());
- if (parent_shell_surface->has_grab_)
- parent_shell_surface->StartCapture();
- }
- widget_->Close();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // views::WidgetObserver overrides:
- void ShellSurfaceBase::OnWidgetClosing(views::Widget* widget) {
- DCHECK(widget_ == widget);
- // To force the widget to close we first disconnect this shell surface from
- // its underlying surface, by asserting to it that the surface destroyed
- // itself. After that, it is safe to call CloseNow() on the widget.
- //
- // TODO(crbug.com/1010326): This only closes the aura/exo pieces, but we
- // should go one level deeper and destroy the wayland stuff. Some options:
- // - Invoke xkill under-the-hood, which will only work for x11 and won't
- // work if the container itself is stuck.
- // - Close the wl connection to the client (i.e. wlkill) this is
- // problematic with X11 as all of xwayland shares the same client.
- // - Transitively kill all the wl_resources rooted at this window's
- // wl_surface, which is not really supported in wayland.
- OnSurfaceDestroying(root_surface());
- }
- ////////////////////////////////////////////////////////////////////////////////
- // views::Views overrides:
- gfx::Size ShellSurfaceBase::CalculatePreferredSize() const {
- if (!geometry_.IsEmpty())
- return geometry_.size();
- // The root surface's content bounds should be used instead of the host window
- // bounds because the host window bounds are not updated until the widget is
- // committed, meaning that if we need to calculate the preferred size before
- // then (e.g. in OnPreWidgetCommit()), then we need to use the root surface's
- // to ensure that we're using the correct bounds' size.
- return root_surface()->surface_hierarchy_content_bounds().size();
- }
- gfx::Size ShellSurfaceBase::GetMinimumSize() const {
- return minimum_size_.IsEmpty() ? gfx::Size(1, 1) : minimum_size_;
- }
- gfx::Size ShellSurfaceBase::GetMaximumSize() const {
- // On ChromeOS, non empty maximum size will make the window
- // non maximizable.
- return maximum_size_;
- }
- void ShellSurfaceBase::GetAccessibleNodeData(ui::AXNodeData* node_data) {
- node_data->role = ax::mojom::Role::kClient;
- if (application_id_) {
- node_data->AddStringAttribute(
- ax::mojom::StringAttribute::kChildTreeNodeAppId, *application_id_);
- }
- }
- views::FocusTraversable* ShellSurfaceBase::GetFocusTraversable() {
- return overlay_widget_.get();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // aura::WindowObserver overrides:
- void ShellSurfaceBase::OnWindowDestroying(aura::Window* window) {
- if (window == parent_)
- SetParentInternal(nullptr);
- window->RemoveObserver(this);
- if (widget_ && window == widget_->GetNativeWindow() && root_surface())
- root_surface()->ThrottleFrameRate(false);
- }
- void ShellSurfaceBase::OnWindowPropertyChanged(aura::Window* window,
- const void* key,
- intptr_t old_value) {
- if (widget_ && window == widget_->GetNativeWindow()) {
- if (key == aura::client::kSkipImeProcessing) {
- SetSkipImeProcessingToDescendentSurfaces(
- window, window->GetProperty(aura::client::kSkipImeProcessing));
- } else if (key == chromeos::kFrameRestoreLookKey) {
- root_surface()->SetFrameLocked(
- window->GetProperty(chromeos::kFrameRestoreLookKey));
- } else if (key == aura::client::kWindowWorkspaceKey) {
- root_surface()->OnDeskChanged(GetWindowDeskStateChanged(window));
- } else if (key == ash::kFrameRateThrottleKey) {
- root_surface()->ThrottleFrameRate(
- window->GetProperty(ash::kFrameRateThrottleKey));
- }
- }
- }
- void ShellSurfaceBase::OnWindowAddedToRootWindow(aura::Window* window) {
- UpdateDisplayOnTree();
- }
- void ShellSurfaceBase::OnWindowParentChanged(aura::Window* window,
- aura::Window* parent) {
- root_surface()->OnDeskChanged(GetWindowDeskStateChanged(window));
- }
- ////////////////////////////////////////////////////////////////////////////////
- // wm::ActivationChangeObserver overrides:
- void ShellSurfaceBase::OnWindowActivated(ActivationReason reason,
- aura::Window* gained_active,
- aura::Window* lost_active) {
- if (!widget_)
- return;
- if (overlay_widget_ && overlay_widget_->widget_delegate()->CanActivate()) {
- aura::client::FocusClient* client =
- aura::client::GetFocusClient(widget_->GetNativeWindow());
- client->ResetFocusWithinActiveWindow(overlay_widget_->GetNativeWindow());
- }
- if (gained_active == widget_->GetNativeWindow() ||
- lost_active == widget_->GetNativeWindow()) {
- DCHECK(gained_active != widget_->GetNativeWindow() || CanActivate());
- UpdateShadow();
- }
- }
- // Returns true if surface is currently being resized.
- bool ShellSurfaceBase::IsDragged() const {
- if (in_extended_drag_)
- return true;
- if (!widget_)
- return false;
- ash::WindowState* window_state =
- ash::WindowState::Get(widget_->GetNativeWindow());
- return window_state ? window_state->is_dragged() : false;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ui::AcceleratorTarget overrides:
- bool ShellSurfaceBase::AcceleratorPressed(const ui::Accelerator& accelerator) {
- for (const auto& entry : kCloseWindowAccelerators) {
- if (ui::Accelerator(entry.keycode, entry.modifiers) == accelerator) {
- OnCloseRequested(views::Widget::ClosedReason::kUnspecified);
- return true;
- }
- }
- return views::View::AcceleratorPressed(accelerator);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ShellSurfaceBase, protected:
- void ShellSurfaceBase::CreateShellSurfaceWidget(
- ui::WindowShowState show_state) {
- DCHECK(GetEnabled());
- DCHECK(!widget_);
- // Sommelier sets the null application id for override redirect windows,
- // which controls its bounds by itself.
- bool emulate_x11_override_redirect =
- !is_popup_ && parent_ && ash::desks_util::IsDeskContainerId(container_) &&
- !application_id_.has_value();
- if (emulate_x11_override_redirect) {
- // override redirect is used for menu, tooltips etc, which should be placed
- // above normal windows, but below lock screen. Specify the container here
- // to avoid using parent_ in params.parent.
- SetContainerInternal(ash::kShellWindowId_ShelfBubbleContainer);
- // X11 override redirect should not be activatable.
- activatable_ = false;
- DisableMovement();
- }
- if (system_modal_)
- SetModalType(ui::MODAL_TYPE_SYSTEM);
- views::Widget::InitParams params;
- params.type = (emulate_x11_override_redirect || is_menu_)
- ? views::Widget::InitParams::TYPE_MENU
- : (is_popup_ ? views::Widget::InitParams::TYPE_POPUP
- : views::Widget::InitParams::TYPE_WINDOW);
- params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
- params.delegate = this;
- params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
- params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
- params.show_state = show_state;
- if (initial_z_order_.has_value())
- params.z_order = initial_z_order_.value();
- if (initial_workspace_.has_value()) {
- const std::string kToggleVisibleOnAllWorkspacesValue = "-1";
- if (initial_workspace_ == kToggleVisibleOnAllWorkspacesValue) {
- // If |initial_workspace_| is -1, window is visible on all workspaces.
- params.visible_on_all_workspaces = true;
- } else {
- params.workspace = initial_workspace_.value();
- }
- }
- // Make shell surface a transient child if |parent_| has been set and
- // container_ isn't specified.
- aura::Window* root_window =
- WMHelper::GetInstance()->GetRootWindowForNewWindows();
- if (ash::desks_util::IsDeskContainerId(container_)) {
- DCHECK_EQ(ash::desks_util::GetActiveDeskContainerId(), container_);
- if (parent_)
- params.parent = parent_;
- else
- params.context = root_window;
- } else {
- params.parent = ash::Shell::GetContainer(root_window, container_);
- }
- if (initial_bounds_)
- params.bounds = *initial_bounds_;
- else
- params.bounds = gfx::Rect(origin_, gfx::Size());
- WMHelper::AppPropertyResolver::Params property_resolver_params;
- if (application_id_)
- property_resolver_params.app_id = *application_id_;
- if (startup_id_)
- property_resolver_params.startup_id = *startup_id_;
- property_resolver_params.for_creation = true;
- WMHelper::GetInstance()->PopulateAppProperties(
- property_resolver_params, params.init_properties_container);
- SetShellApplicationId(¶ms.init_properties_container, application_id_);
- SetShellRootSurface(¶ms.init_properties_container, root_surface());
- SetShellStartupId(¶ms.init_properties_container, startup_id_);
- bool activatable = activatable_;
- // ShellSurfaces in system modal container are only activatable if input
- // region is non-empty. See OnCommitSurface() for more details.
- if (container_ == ash::kShellWindowId_SystemModalContainer)
- activatable &= HasHitTestRegion();
- // Transient child needs to have an application id to be activatable.
- if (parent_)
- activatable &= application_id_.has_value();
- params.activatable = activatable
- ? views::Widget::InitParams::Activatable::kYes
- : views::Widget::InitParams::Activatable::kNo;
- if (restore_session_id_) {
- params.init_properties_container.SetProperty(app_restore::kWindowIdKey,
- *restore_session_id_);
- }
- if (restore_window_id_) {
- params.init_properties_container.SetProperty(
- app_restore::kRestoreWindowIdKey, *restore_window_id_);
- }
- if (restore_window_id_source_) {
- params.init_properties_container.SetProperty(
- app_restore::kRestoreWindowIdKey,
- app_restore::FetchRestoreWindowId(*restore_window_id_source_));
- params.init_properties_container.SetProperty(
- app_restore::kAppIdKey, restore_window_id_source_.value());
- }
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- // Restore `params` to those of the saved `restore_window_id_`.
- app_restore::ModifyWidgetParams(params.init_properties_container.GetProperty(
- app_restore::kRestoreWindowIdKey),
- ¶ms);
- #endif
- // If app restore specifies the initial bounds, set `initial_bounds_` to it so
- // that shell surface knows the initial bounds is set.
- if (!params.bounds.IsEmpty() && !initial_bounds_)
- initial_bounds_.emplace(params.bounds);
- OverrideInitParams(¶ms);
- // Note: NativeWidget owns this widget.
- widget_ = new ShellSurfaceWidget;
- widget_->Init(std::move(params));
- widget_->AddObserver(this);
- // As setting the pinned mode may have come in earlier we apply it now.
- UpdatePinned();
- aura::Window* window = widget_->GetNativeWindow();
- window->SetName(base::StringPrintf("ExoShellSurface-%d", shell_id++));
- window->AddChild(host_window());
- // Works for both mash and non-mash. https://crbug.com/839521
- window->SetEventTargetingPolicy(
- aura::EventTargetingPolicy::kTargetAndDescendants);
- InstallCustomWindowTargeter();
- // Start tracking changes to window bounds and window state.
- window->AddObserver(this);
- ash::WindowState* window_state = ash::WindowState::Get(window);
- // Skip initializing window state when it is menu.
- // TODO(crbug.com/1338597): Remove `window_state` condition when tooltip fix
- // is done. Without the fix, window_state can be null when it is tooltip and
- // the parent window is menu, so add null check of `window_state` here.
- if (!is_menu_ && window_state)
- InitializeWindowState(window_state);
- SetShellUseImmersiveForFullscreen(window, immersive_implied_by_fullscreen_);
- // Fade visibility animations for non-activatable windows.
- if (!CanActivate()) {
- wm::SetWindowVisibilityAnimationType(
- window, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
- }
- // Register close window accelerators.
- views::FocusManager* focus_manager = widget_->GetFocusManager();
- for (const auto& entry : kCloseWindowAccelerators) {
- focus_manager->RegisterAccelerator(
- ui::Accelerator(entry.keycode, entry.modifiers),
- ui::AcceleratorManager::kNormalPriority, this);
- }
- // Show widget next time Commit() is called.
- if (show_state != ui::SHOW_STATE_MINIMIZED)
- pending_show_widget_ = true;
- UpdateDisplayOnTree();
- if (frame_type_ != SurfaceFrameType::NONE)
- OnSetFrame(frame_type_);
- if (pending_pip_)
- SetPip();
- root_surface()->OnDeskChanged(GetWindowDeskStateChanged(window));
- WMHelper::GetInstance()->NotifyExoWindowCreated(widget_->GetNativeWindow());
- }
- ShellSurfaceBase::OverlayParams::OverlayParams(
- std::unique_ptr<views::View> overlay)
- : contents_view(std::move(overlay)) {}
- ShellSurfaceBase::OverlayParams::~OverlayParams() = default;
- bool ShellSurfaceBase::IsResizing() const {
- ash::WindowState* window_state =
- ash::WindowState::Get(widget_->GetNativeWindow());
- if (!window_state || !window_state->is_dragged())
- return false;
- return window_state->drag_details() &&
- (window_state->drag_details()->bounds_change &
- ash::WindowResizer::kBoundsChange_Resizes);
- }
- gfx::Rect ShellSurfaceBase::ComputeAdjustedBounds(
- const gfx::Rect& bounds) const {
- return bounds;
- }
- void ShellSurfaceBase::UpdateWidgetBounds() {
- DCHECK(widget_);
- absl::optional<gfx::Rect> bounds = GetWidgetBounds();
- if (!bounds)
- return;
- gfx::Rect adjusted_bounds = ComputeAdjustedBounds(*bounds);
- if (overlay_widget_) {
- gfx::Rect content_bounds(adjusted_bounds.size());
- int height = 0;
- if (!overlay_overlaps_frame_ && frame_enabled()) {
- auto* frame_view = static_cast<const ash::NonClientFrameViewAsh*>(
- widget_->non_client_view()->frame_view());
- height = frame_view->NonClientTopBorderHeight();
- }
- content_bounds.set_height(content_bounds.height() - height);
- content_bounds.set_y(height);
- overlay_widget_->SetBounds(content_bounds);
- }
- aura::Window* window = widget_->GetNativeWindow();
- ash::WindowState* window_state = ash::WindowState::Get(window);
- // Return early if the shell is currently managing the bounds of the widget.
- if (window_state && !window_state->allow_set_bounds_direct()) {
- // 1) When a window is either maximized/fullscreen/pinned.
- if (window_state->IsMaximizedOrFullscreenOrPinned())
- return;
- // 2) When a window is snapped.
- if (window_state->IsSnapped())
- return;
- // 3) When a window is being interactively resized.
- if (IsResizing())
- return;
- // 4) When a window's bounds are being animated.
- if (window->layer()->GetAnimator()->IsAnimatingProperty(
- ui::LayerAnimationElement::BOUNDS))
- return;
- }
- SetWidgetBounds(adjusted_bounds, adjusted_bounds != *bounds);
- }
- void ShellSurfaceBase::UpdateSurfaceBounds() {
- gfx::Point origin = GetClientViewBounds().origin();
- origin += GetSurfaceOrigin().OffsetFromOrigin();
- origin -= ToFlooredVector2d(ScaleVector2d(
- root_surface_origin().OffsetFromOrigin(), 1.f / GetScale()));
- gfx::Rect surface_bounds(origin, host_window()->bounds().size());
- if (host_window()->bounds() == surface_bounds)
- return;
- host_window()->SetBounds(surface_bounds);
- }
- void ShellSurfaceBase::UpdateShadow() {
- if (!widget_ || !root_surface())
- return;
- aura::Window* window = widget_->GetNativeWindow();
- if (!shadow_bounds_) {
- wm::SetShadowElevation(window, wm::kShadowElevationNone);
- } else {
- // Use a small style shadow for popup surface.
- if (frame_type_ == SurfaceFrameType::SHADOW && is_popup_)
- wm::SetShadowElevation(window, wm::kShadowElevationMenuOrTooltip);
- else
- wm::SetShadowElevation(window, wm::kShadowElevationDefault);
- ui::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window);
- // Maximized/Fullscreen window does not create a shadow.
- if (!shadow)
- return;
- gfx::Rect shadow_bounds = GetShadowBounds();
- gfx::Point origin = GetClientViewBounds().origin();
- if (!window->GetProperty(aura::client::kUseWindowBoundsForShadow)) {
- origin += GetSurfaceOrigin().OffsetFromOrigin();
- origin -= ToFlooredVector2d(ScaleVector2d(
- root_surface_origin().OffsetFromOrigin(), 1.f / GetScale()));
- if (origin.x() != 0 || origin.y() != 0) {
- shadow_bounds.set_origin(origin);
- if (widget_) {
- gfx::Point widget_origin =
- widget_->GetWindowBoundsInScreen().origin();
- origin += ToFlooredVector2d(
- ScaleVector2d(gfx::Vector2d(widget_origin.x(), widget_origin.y()),
- 1.f / GetScale()));
- gfx::Rect bounds = geometry_;
- bounds.set_origin(origin);
- ash::Shell::Get()
- ->resize_shadow_controller()
- ->UpdateResizeShadowBoundsOfWindow(widget_->GetNativeWindow(),
- bounds);
- }
- }
- }
- shadow->SetContentBounds(shadow_bounds);
- // Surfaces that can't be activated are usually menus and tooltips. Use a
- // small style shadow for them.
- if (!CanActivate())
- shadow->SetElevation(wm::kShadowElevationMenuOrTooltip);
- UpdateCornerRadius();
- }
- }
- void ShellSurfaceBase::UpdateCornerRadius() {
- if (!widget_)
- return;
- if (!ash::features::IsPipRoundedCornersEnabled())
- return;
- ash::WindowState* window_state =
- ash::WindowState::Get(widget_->GetNativeWindow());
- // The host window's transform scales by |1/GetScale()| but we do not want the
- // rounded corners scaled that way. So we multiply the radius by |GetScale()|.
- if (window_state) {
- ash::SetCornerRadius(
- window_state->window(), host_window()->layer(),
- window_state->IsPip()
- ? base::ClampRound(GetScale() * chromeos::kPipRoundedCornerRadius)
- : 0);
- }
- }
- void ShellSurfaceBase::UpdateFrameType() {
- // Nothing to do here for now as frame type is updated immediately in
- // OnSetFrame() by default.
- }
- gfx::Rect ShellSurfaceBase::GetVisibleBounds() const {
- // Use |geometry_| if set, otherwise use the visual bounds of the surface.
- if (geometry_.IsEmpty()) {
- gfx::Size size;
- if (root_surface()) {
- float int_part;
- DCHECK(std::modf(root_surface()->content_size().width(), &int_part) ==
- 0.0f &&
- std::modf(root_surface()->content_size().height(), &int_part) ==
- 0.0f);
- size = gfx::ToCeiledSize(root_surface()->content_size());
- if (client_submits_surfaces_in_pixel_coordinates()) {
- float dsf = host_window()->layer()->device_scale_factor();
- size = gfx::ScaleToRoundedSize(size, 1.0f / dsf);
- }
- }
- return gfx::Rect(size);
- }
- const auto* screen = display::Screen::GetScreen();
- display::Display display;
- if (!screen->GetDisplayWithDisplayId(display_id_, &display))
- return geometry_;
- // Convert from display to screen coordinates.
- return geometry_ + display.bounds().OffsetFromOrigin();
- }
- gfx::Rect ShellSurfaceBase::GetClientViewBounds() const {
- return widget_->non_client_view()
- ? widget_->non_client_view()
- ->frame_view()
- ->GetBoundsForClientView()
- : gfx::Rect(widget_->GetWindowBoundsInScreen().size());
- }
- gfx::Rect ShellSurfaceBase::GetWidgetBoundsFromVisibleBounds() const {
- auto visible_bounds = GetVisibleBounds();
- return widget_->non_client_view()
- ? widget_->non_client_view()->GetWindowBoundsForClientBounds(
- visible_bounds)
- : visible_bounds;
- }
- gfx::Rect ShellSurfaceBase::GetShadowBounds() const {
- return shadow_bounds_->IsEmpty()
- ? gfx::Rect(widget_->GetNativeWindow()->bounds().size())
- : gfx::ScaleToEnclosedRect(*shadow_bounds_, 1.f / GetScale());
- }
- void ShellSurfaceBase::InstallCustomWindowTargeter() {
- aura::Window* window = widget_->GetNativeWindow();
- window->SetEventTargeter(std::make_unique<CustomWindowTargeter>(this));
- }
- std::unique_ptr<views::NonClientFrameView>
- ShellSurfaceBase::CreateNonClientFrameViewInternal(views::Widget* widget) {
- aura::Window* window = widget_->GetNativeWindow();
- // ShellSurfaces always use immersive mode.
- window->SetProperty(chromeos::kImmersiveIsActive, true);
- ash::WindowState* window_state = ash::WindowState::Get(window);
- if (!frame_enabled() && window_state && !window_state->HasDelegate()) {
- window_state->SetDelegate(std::make_unique<CustomWindowStateDelegate>());
- }
- auto frame_view =
- std::make_unique<CustomFrameView>(widget, this, frame_enabled());
- if (has_frame_colors_)
- frame_view->SetFrameColors(active_frame_color_, inactive_frame_color_);
- return frame_view;
- }
- bool ShellSurfaceBase::ShouldExitFullscreenFromRestoreOrMaximized() {
- if (widget_ && widget_->GetNativeWindow()) {
- return widget_->GetNativeWindow()->GetProperty(
- kRestoreOrMaximizeExitsFullscreen);
- }
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ShellSurfaceBase, private:
- float ShellSurfaceBase::GetScale() const {
- return 1.f;
- }
- void ShellSurfaceBase::StartCapture() {
- widget_->set_auto_release_capture(false);
- WMHelper::GetInstance()->GetCaptureClient()->AddObserver(this);
- // Just capture on the window.
- widget_->SetCapture(nullptr /* view */);
- }
- void ShellSurfaceBase::OnPostWidgetCommit() {
- // |shadow_bounds_changed_| represents whether |shadow_bounds_| has changed
- // since the last commit, but as UpdateShadow() can be called multiple times
- // in a single commit process, we need to ensure that it's not reset halfway
- // in the current commit by resetting it here.
- shadow_bounds_changed_ = false;
- }
- void ShellSurfaceBase::SetContainerInternal(int container) {
- container_ = container;
- WidgetDelegate::SetCanMaximize(
- !parent_ && ash::desks_util::IsDeskContainerId(container_));
- if (widget_)
- widget_->OnSizeConstraintsChanged();
- }
- void ShellSurfaceBase::SetParentInternal(aura::Window* parent) {
- parent_ = parent;
- WidgetDelegate::SetCanMinimize(!parent_ && can_minimize_);
- UpdateResizability();
- if (widget_)
- widget_->OnSizeConstraintsChanged();
- }
- bool ShellSurfaceBase::CalculateCanResize() const {
- if (overlay_widget_ && overlay_can_resize_.has_value())
- return *overlay_can_resize_;
- return !movement_disabled_ && GetCanResizeFromSizeConstraints();
- }
- void ShellSurfaceBase::CommitWidget() {
- // Apply new window geometry.
- geometry_ = pending_geometry_;
- display_id_ = pending_display_id_;
- // Apply new minimum/maximium size.
- bool size_constraint_changed = minimum_size_ != pending_minimum_size_ ||
- maximum_size_ != pending_maximum_size_;
- minimum_size_ = pending_minimum_size_;
- maximum_size_ = pending_maximum_size_;
- UpdateResizability();
- if (!widget_)
- return;
- if (!pending_aspect_ratio_.IsEmpty()) {
- widget_->SetAspectRatio(pending_aspect_ratio_);
- } else if (widget_->GetNativeWindow()) {
- // TODO(yoshiki): Move the logic to clear aspect ratio into view::Widget.
- widget_->GetNativeWindow()->ClearProperty(aura::client::kAspectRatio);
- }
- UpdateWidgetBounds();
- SurfaceTreeHost::UpdateHostWindowBounds();
- UpdateFrameType();
- gfx::Rect bounds = geometry_;
- if (!bounds.IsEmpty() && !widget_->GetNativeWindow()->GetProperty(
- aura::client::kUseWindowBoundsForShadow)) {
- SetBoundsForShadows(absl::make_optional(bounds));
- }
- UpdateShadow();
- // System modal container is used by clients to implement overlay
- // windows using a single ShellSurface instance. If hit-test
- // region is empty, then it is non interactive window and won't be
- // activated.
- if (container_ == ash::kShellWindowId_SystemModalContainer) {
- // Prevent window from being activated when hit test region is empty.
- bool activatable = activatable_ && HasHitTestRegion();
- if (activatable != CanActivate()) {
- SetCanActivate(activatable);
- // Activate or deactivate window if activation state changed.
- if (activatable) {
- // Automatically activate only if the window is modal.
- // Non modal window should be activated by a user action.
- // TODO(oshima): Non modal system window does not have an associated
- // task ID, and as a result, it cannot be activated from client side.
- // Fix this (b/65460424) and remove this if condition.
- if (system_modal_)
- wm::ActivateWindow(widget_->GetNativeWindow());
- } else if (widget_->IsActive()) {
- wm::DeactivateWindow(widget_->GetNativeWindow());
- }
- }
- }
- UpdateSurfaceBounds();
- // Don't show yet if the shell surface doesn't have content or is minimized
- // while waiting for content.
- bool should_show =
- !host_window()->bounds().IsEmpty() && !widget_->IsMinimized();
- // Do not layout the window if the position should not be controlled by window
- // manager. (popup, emulating x11 override direct, or requested not to move)
- if (is_popup_ || movement_disabled_)
- needs_layout_on_show_ = false;
- // Do not center if the initial bounds is set.
- if (initial_bounds_)
- needs_layout_on_show_ = false;
- // Show widget if needed.
- if (pending_show_widget_ && should_show) {
- DCHECK(!widget_->IsClosed());
- DCHECK(!widget_->IsVisible());
- pending_show_widget_ = false;
- auto* window = widget_->GetNativeWindow();
- auto* window_state = ash::WindowState::Get(window);
- // TODO(crbug.com/1261321): correct the initial origin once lacros can
- // communicate it instead of centering.
- if (window_state && window_state->IsMaximizedOrFullscreenOrPinned()) {
- gfx::Size current_content_size = CalculatePreferredSize();
- gfx::Rect restore_bounds = display::Screen::GetScreen()
- ->GetDisplayNearestWindow(window)
- .work_area();
- if (!current_content_size.IsEmpty())
- restore_bounds.ClampToCenteredSize(current_content_size);
- window_state->SetRestoreBoundsInScreen(restore_bounds);
- }
- // TODO(crbug.com/1291592): Hook this up with the WM's window positioning
- // logic.
- if (needs_layout_on_show_) {
- widget_->CenterWindow(GetWidgetBoundsFromVisibleBounds().size());
- needs_layout_on_show_ = false;
- }
- widget_->Show();
- if (has_grab_)
- StartCapture();
- if (container_ == ash::kShellWindowId_SystemModalContainer)
- UpdateSystemModal();
- }
- if (size_constraint_changed)
- widget_->OnSizeConstraintsChanged();
- }
- bool ShellSurfaceBase::IsFrameDecorationSupported(SurfaceFrameType frame_type) {
- if (!is_popup_)
- return true;
- // Popup doesn't support frame types other than NONE/SHADOW.
- return frame_type == SurfaceFrameType::SHADOW ||
- frame_type == SurfaceFrameType::NONE;
- }
- void ShellSurfaceBase::SetOrientationLock(
- chromeos::OrientationType orientation_lock) {
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- TRACE_EVENT1("exo", "ShellSurfaceBase::SetOrientationLock",
- "orientation_lock", static_cast<int>(orientation_lock));
- if (!widget_) {
- initial_orientation_lock_ = orientation_lock;
- return;
- }
- ash::Shell* shell = ash::Shell::Get();
- shell->screen_orientation_controller()->LockOrientationForWindow(
- widget_->GetNativeWindow(), orientation_lock);
- #else
- NOTREACHED();
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- }
- void ShellSurfaceBase::SetZOrder(ui::ZOrderLevel z_order) {
- // If there is already a widget, we can immediately set its z order.
- if (widget_) {
- widget_->SetZOrderLevel(z_order);
- return;
- }
- // Otherwise, we want to save `z_order` for when `widget_` is initialized.
- initial_z_order_ = z_order;
- }
- } // namespace exo
|