// Copyright 2016 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/notification_surface.h" #include #include "components/exo/notification_surface_manager.h" #include "components/exo/shell_surface_util.h" #include "components/exo/surface.h" #include "components/viz/host/host_frame_sink_manager.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size_conversions.h" namespace exo { NotificationSurface::NotificationSurface(NotificationSurfaceManager* manager, Surface* surface, const std::string& notification_key) : SurfaceTreeHost("ExoNotificationSurface"), manager_(manager), notification_key_(notification_key) { surface->AddSurfaceObserver(this); SetRootSurface(surface); host_window()->Show(); host_window()->AddObserver(this); } NotificationSurface::~NotificationSurface() { if (host_window()) host_window()->RemoveObserver(this); if (added_to_manager_) manager_->RemoveSurface(this); if (root_surface()) root_surface()->RemoveSurfaceObserver(this); } gfx::Size NotificationSurface::GetContentSize() const { 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); return gfx::ToRoundedSize(root_surface()->content_size()); } void NotificationSurface::SetApplicationId(const char* application_id) { SetShellApplicationId(host_window(), absl::make_optional(application_id)); } void NotificationSurface::OnSurfaceCommit() { SurfaceTreeHost::OnSurfaceCommit(); // Defer AddSurface until there are contents to show. if (!added_to_manager_ && !host_window()->bounds().IsEmpty()) { added_to_manager_ = true; manager_->AddSurface(this); } // Only submit a compositor frame if the notification is being shown. // Submitting compositor frames while invisible causes Viz to hold on to // references to each notification buffer while it waits for an embedding ( // or a timeout occurs). This can cause buffer starvation on the Android side, // leading to ANRs. if (is_embedded_) SubmitCompositorFrame(); } void NotificationSurface::OnSurfaceDestroying(Surface* surface) { DCHECK_EQ(surface, root_surface()); surface->RemoveSurfaceObserver(this); SetRootSurface(nullptr); } void NotificationSurface::OnWindowDestroying(aura::Window* window) { window->RemoveObserver(this); } void NotificationSurface::OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old_value) { if (key == aura::client::kSkipImeProcessing) { SetSkipImeProcessingToDescendentSurfaces( window, window->GetProperty(aura::client::kSkipImeProcessing)); } } void NotificationSurface::OnWindowAddedToRootWindow(aura::Window* window) { // Force recreating resources to submit the compositor frame w/o // commit request. root_surface()->SurfaceHierarchyResourcesLost(); SubmitCompositorFrame(); is_embedded_ = true; } void NotificationSurface::OnWindowRemovingFromRootWindow( aura::Window* window, aura::Window* new_root) { if (!new_root) { // Submit an empty compositor frame if the notification becomes invisible to // notify Viz that it can release its reference to the existing notification // buffer. We can't just evict the surface, because LayerTreeFrameSinkHolder // needs to hold on to resources that were used in the previous frame, if it // was associated with a different local surface id. SubmitEmptyCompositorFrame(); // Evict the current surface. We can't only submit // an empty compositor frame, because then when re-opening the message // center, Viz may think it can use the empty compositor frame to display. // This will force viz to wait the given deadline for the next notification // compositor frame when showing the message center. This is to prevent // flashes when opening the message center. aura::Env::GetInstance() ->context_factory() ->GetHostFrameSinkManager() ->EvictSurfaces({host_window()->GetSurfaceId()}); // Allocate a new local surface id for the next compositor frame. host_window()->AllocateLocalSurfaceId(); is_embedded_ = false; // Set the deadline to default so Viz waits a while for the notification // compositor frame to arrive before showing the message center. host_window()->layer()->SetShowSurface( host_window()->GetSurfaceId(), host_window()->bounds().size(), SK_ColorWHITE, cc::DeadlinePolicy::UseDefaultDeadline(), /*stretch_content_to_fill_bounds=*/false); } } } // namespace exo