| // Copyright 2021 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "ash/utility/layer_copy_animator.h" | 
 |  | 
 | #include "ash/utility/layer_util.h" | 
 | #include "base/functional/bind.h" | 
 | #include "ui/aura/window.h" | 
 | #include "ui/base/class_property.h" | 
 | #include "ui/compositor/layer_animation_sequence.h" | 
 | #include "ui/compositor/layer_animator.h" | 
 | #include "ui/compositor/layer_type.h" | 
 |  | 
 | DEFINE_UI_CLASS_PROPERTY_TYPE(ash::LayerCopyAnimator*) | 
 |  | 
 | namespace ash { | 
 | namespace { | 
 |  | 
 | DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(LayerCopyAnimator, kLayerCopyAnimatorKey) | 
 |  | 
 | // CopyOutputRequest's callback may be called on the different thread during | 
 | // shutdown, which results in the DCHECK failure in the weak ptr when | 
 | // referenced. | 
 | void MaybeLayerCopied(base::WeakPtr<LayerCopyAnimator> swc, | 
 |                       std::unique_ptr<ui::Layer> new_layer) { | 
 |   if (swc) { | 
 |     swc->OnLayerCopied(std::move(new_layer)); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | LayerCopyAnimator* LayerCopyAnimator::Get(aura::Window* window) { | 
 |   return window->GetProperty(kLayerCopyAnimatorKey); | 
 | } | 
 |  | 
 | LayerCopyAnimator::LayerCopyAnimator(aura::Window* window) : window_(window) { | 
 |   window->SetProperty(kLayerCopyAnimatorKey, this); | 
 |   observation_.Observe(window); | 
 |  | 
 |   // Copy request will not copy NOT_DRAWN and the result may be smaller than | 
 |   // requested layer.  Create a transparent layer to cover the entire layer. | 
 |   if (window_->layer()->type() == ui::LAYER_NOT_DRAWN) { | 
 |     full_layer_.SetColor(SK_ColorTRANSPARENT); | 
 |     full_layer_.SetBounds(gfx::Rect(window_->bounds().size())); | 
 |     window_->layer()->Add(&full_layer_); | 
 |     window_->layer()->StackAtBottom(&full_layer_); | 
 |   } | 
 |   window_->layer()->GetAnimator()->StopAnimating(); | 
 |   window_->layer()->SetOpacity(1.f); | 
 |  | 
 |   CopyLayerContentToNewLayer( | 
 |       window_->layer(), | 
 |       base::BindOnce(&MaybeLayerCopied, weak_ptr_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | LayerCopyAnimator::~LayerCopyAnimator() { | 
 |   window_->layer()->SetOpacity(1.0f); | 
 |   if (fake_sequence_) | 
 |     NotifyWithFakeSequence(/*abort=*/true); | 
 |   DCHECK(!observer_); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::MaybeStartAnimation( | 
 |     ui::LayerAnimationObserver* observer, | 
 |     AnimationCallback callback) { | 
 |   DCHECK(!animation_requested_); | 
 |   observer_ = observer; | 
 |   animation_callback_ = std::move(callback); | 
 |   animation_requested_ = true; | 
 |   if (fail_) { | 
 |     FinishAndDelete(/*abort=*/true); | 
 |     return; | 
 |   } | 
 |   if (copied_layer_) { | 
 |     RunAnimation(); | 
 |     return; | 
 |   } | 
 |   if (observer_) | 
 |     EnsureFakeSequence(); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::OnLayerCopied(std::unique_ptr<ui::Layer> new_layer) { | 
 |   if (fail_) | 
 |     return; | 
 |  | 
 |   if (!new_layer) | 
 |     fail_ = true; | 
 |  | 
 |   copied_layer_ = std::move(new_layer); | 
 |   if (animation_requested_ && !fail_) | 
 |     RunAnimation(); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::OnLayerAnimationEnded( | 
 |     ui::LayerAnimationSequence* sequence) { | 
 |   if (last_sequence_ == sequence) | 
 |     window_->ClearProperty(kLayerCopyAnimatorKey); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::OnLayerAnimationAborted( | 
 |     ui::LayerAnimationSequence* sequence) { | 
 |   window_->ClearProperty(kLayerCopyAnimatorKey); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::OnWindowBoundsChanged(aura::Window* window, | 
 |                                               const gfx::Rect& old_bounds, | 
 |                                               const gfx::Rect& new_bounds, | 
 |                                               ui::PropertyChangeReason reason) { | 
 |   fail_ = true; | 
 |   if (copied_layer_) | 
 |     copied_layer_->GetAnimator()->StopAnimating(); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::RunAnimation() { | 
 |   CHECK_EQ(copied_layer_->type(), ui::LAYER_SOLID_COLOR); | 
 |  | 
 |   auto* parent_layer = window_->layer()->parent(); | 
 |   parent_layer->Add(copied_layer_.get()); | 
 |   parent_layer->StackAbove(copied_layer_.get(), window_->layer()); | 
 |   window_->layer()->SetOpacity(0.f); | 
 |  | 
 |   std::move(animation_callback_).Run(copied_layer_.get(), observer_.get()); | 
 |  | 
 |   // Callback may not run animations, in which case, just end immediately. | 
 |   if (!copied_layer_->GetAnimator()->is_animating()) { | 
 |     FinishAndDelete(/*abort=*/false); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (fake_sequence_) | 
 |     NotifyWithFakeSequence(/*abort=*/false); | 
 |  | 
 |   observer_ = nullptr; | 
 |   last_sequence_ = new ui::LayerAnimationSequence( | 
 |       ui::LayerAnimationElement::CreateOpacityElement(1.0, base::TimeDelta())); | 
 |   copied_layer_->GetAnimator()->ScheduleAnimation(last_sequence_); | 
 |   copied_layer_->GetAnimator()->AddObserver(this); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::FinishAndDelete(bool abort) { | 
 |   if (observer_) { | 
 |     EnsureFakeSequence(); | 
 |     NotifyWithFakeSequence(abort); | 
 |   } | 
 |   window_->ClearProperty(kLayerCopyAnimatorKey); | 
 | } | 
 |  | 
 | void LayerCopyAnimator::NotifyWithFakeSequence(bool abort) { | 
 |   DCHECK(fake_sequence_); | 
 |   if (abort) | 
 |     observer_->OnLayerAnimationAborted(fake_sequence_.get()); | 
 |   else | 
 |     observer_->OnLayerAnimationEnded(fake_sequence_.get()); | 
 |   fake_sequence_.reset(); | 
 |   observer_ = nullptr; | 
 | } | 
 |  | 
 | void LayerCopyAnimator::EnsureFakeSequence() { | 
 |   if (fake_sequence_) | 
 |     return; | 
 |   fake_sequence_ = std::make_unique<ui::LayerAnimationSequence>( | 
 |       ui::LayerAnimationElement::CreateOpacityElement(0.0, base::TimeDelta())); | 
 |   fake_sequence_->AddObserver(observer_); | 
 | } | 
 |  | 
 | }  // namespace ash |