Home | History | Annotate | Download | only in aura
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/web_contents/aura/overscroll_navigation_overlay.h"
      6 
      7 #include "content/browser/frame_host/navigation_entry_impl.h"
      8 #include "content/browser/renderer_host/render_view_host_impl.h"
      9 #include "content/browser/web_contents/aura/image_window_delegate.h"
     10 #include "content/browser/web_contents/web_contents_impl.h"
     11 #include "content/common/view_messages.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "content/public/browser/render_widget_host_view.h"
     14 #include "ui/aura/window.h"
     15 #include "ui/base/layout.h"
     16 #include "ui/compositor/layer.h"
     17 #include "ui/compositor/layer_animation_observer.h"
     18 #include "ui/compositor/scoped_layer_animation_settings.h"
     19 #include "ui/gfx/canvas.h"
     20 #include "ui/gfx/image/image_png_rep.h"
     21 #include "ui/gfx/image/image_skia.h"
     22 
     23 namespace content {
     24 namespace {
     25 
     26 // Returns true if the entry's URL or any of the URLs in entry's redirect chain
     27 // match |url|.
     28 bool DoesEntryMatchURL(NavigationEntry* entry, const GURL& url) {
     29   if (entry->GetURL() == url)
     30     return true;
     31   const std::vector<GURL>& redirect_chain = entry->GetRedirectChain();
     32   for (std::vector<GURL>::const_iterator it = redirect_chain.begin();
     33        it != redirect_chain.end();
     34        it++) {
     35     if (*it == url)
     36       return true;
     37   }
     38   return false;
     39 }
     40 
     41 }  // namespace
     42 
     43 // A LayerDelegate that paints an image for the layer.
     44 class ImageLayerDelegate : public ui::LayerDelegate {
     45  public:
     46   ImageLayerDelegate() {}
     47 
     48   virtual ~ImageLayerDelegate() {}
     49 
     50   void SetImage(const gfx::Image& image) {
     51     image_ = image;
     52     image_size_ = image.AsImageSkia().size();
     53   }
     54   const gfx::Image& image() const { return image_; }
     55 
     56  private:
     57   // Overridden from ui::LayerDelegate:
     58   virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
     59     if (image_.IsEmpty()) {
     60       canvas->DrawColor(SK_ColorWHITE);
     61     } else {
     62       SkISize size = canvas->sk_canvas()->getDeviceSize();
     63       if (size.width() != image_size_.width() ||
     64           size.height() != image_size_.height()) {
     65         canvas->DrawColor(SK_ColorWHITE);
     66       }
     67       canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
     68     }
     69   }
     70 
     71   virtual void OnDelegatedFrameDamage(
     72       const gfx::Rect& damage_rect_in_dip) OVERRIDE {}
     73 
     74   // Called when the layer's device scale factor has changed.
     75   virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
     76   }
     77 
     78   // Invoked prior to the bounds changing. The returned closured is run after
     79   // the bounds change.
     80   virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
     81     return base::Closure();
     82   }
     83 
     84   gfx::Image image_;
     85   gfx::Size image_size_;
     86 
     87   DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
     88 };
     89 
     90 // Responsible for fading out and deleting the layer of the overlay window.
     91 class OverlayDismissAnimator
     92     : public ui::LayerAnimationObserver {
     93  public:
     94   // Takes ownership of the layer.
     95   explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer)
     96       : layer_(layer.Pass()) {
     97     CHECK(layer_.get());
     98   }
     99 
    100   // Starts the fadeout animation on the layer. When the animation finishes,
    101   // the object deletes itself along with the layer.
    102   void Animate() {
    103     DCHECK(layer_.get());
    104     ui::LayerAnimator* animator = layer_->GetAnimator();
    105     // This makes SetOpacity() animate with default duration (which could be
    106     // zero, e.g. when running tests).
    107     ui::ScopedLayerAnimationSettings settings(animator);
    108     animator->AddObserver(this);
    109     layer_->SetOpacity(0);
    110   }
    111 
    112   // Overridden from ui::LayerAnimationObserver
    113   virtual void OnLayerAnimationEnded(
    114       ui::LayerAnimationSequence* sequence) OVERRIDE {
    115     delete this;
    116   }
    117 
    118   virtual void OnLayerAnimationAborted(
    119       ui::LayerAnimationSequence* sequence) OVERRIDE {
    120     delete this;
    121   }
    122 
    123   virtual void OnLayerAnimationScheduled(
    124       ui::LayerAnimationSequence* sequence) OVERRIDE {}
    125 
    126  private:
    127   virtual ~OverlayDismissAnimator() {}
    128 
    129   scoped_ptr<ui::Layer> layer_;
    130 
    131   DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator);
    132 };
    133 
    134 OverscrollNavigationOverlay::OverscrollNavigationOverlay(
    135     WebContentsImpl* web_contents)
    136     : web_contents_(web_contents),
    137       image_delegate_(NULL),
    138       loading_complete_(false),
    139       received_paint_update_(false),
    140       slide_direction_(SLIDE_UNKNOWN) {
    141 }
    142 
    143 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
    144 }
    145 
    146 void OverscrollNavigationOverlay::StartObserving() {
    147   loading_complete_ = false;
    148   received_paint_update_ = false;
    149   overlay_dismiss_layer_.reset();
    150   Observe(web_contents_);
    151 
    152   // Make sure the overlay window is on top.
    153   if (window_.get() && window_->parent())
    154     window_->parent()->StackChildAtTop(window_.get());
    155 
    156   // Assumes the navigation has been initiated.
    157   NavigationEntry* pending_entry =
    158       web_contents_->GetController().GetPendingEntry();
    159   // Save url of the pending entry to identify when it loads and paints later.
    160   // Under some circumstances navigation can leave a null pending entry -
    161   // see comments in NavigationControllerImpl::NavigateToPendingEntry().
    162   pending_entry_url_ = pending_entry ? pending_entry->GetURL() : GURL();
    163 }
    164 
    165 void OverscrollNavigationOverlay::SetOverlayWindow(
    166     scoped_ptr<aura::Window> window,
    167     ImageWindowDelegate* delegate) {
    168   window_ = window.Pass();
    169   if (window_.get() && window_->parent())
    170     window_->parent()->StackChildAtTop(window_.get());
    171   image_delegate_ = delegate;
    172 
    173   if (window_.get() && delegate->has_image()) {
    174     window_slider_.reset(new WindowSlider(this,
    175                                           window_->parent(),
    176                                           window_.get()));
    177     slide_direction_ = SLIDE_UNKNOWN;
    178   } else {
    179     window_slider_.reset();
    180   }
    181 }
    182 
    183 void OverscrollNavigationOverlay::StopObservingIfDone() {
    184   // Normally we dismiss the overlay once we receive a paint update, however
    185   // for in-page navigations DidFirstVisuallyNonEmptyPaint() does not get
    186   // called, and we rely on loading_complete_ for those cases.
    187   if (!received_paint_update_ && !loading_complete_)
    188     return;
    189 
    190   // If a slide is in progress, then do not destroy the window or the slide.
    191   if (window_slider_.get() && window_slider_->IsSlideInProgress())
    192     return;
    193 
    194   // The layer to be animated by OverlayDismissAnimator
    195   scoped_ptr<ui::Layer> overlay_dismiss_layer;
    196   if (overlay_dismiss_layer_)
    197     overlay_dismiss_layer = overlay_dismiss_layer_.Pass();
    198   else if (window_.get())
    199     overlay_dismiss_layer = window_->AcquireLayer();
    200   Observe(NULL);
    201   window_slider_.reset();
    202   window_.reset();
    203   image_delegate_ = NULL;
    204   if (overlay_dismiss_layer.get()) {
    205     // OverlayDismissAnimator deletes overlay_dismiss_layer and itself when the
    206     // animation completes.
    207     (new OverlayDismissAnimator(overlay_dismiss_layer.Pass()))->Animate();
    208   }
    209 }
    210 
    211 ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
    212   const NavigationControllerImpl& controller = web_contents_->GetController();
    213   const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
    214       controller.GetEntryAtOffset(offset));
    215 
    216   gfx::Image image;
    217   if (entry && entry->screenshot().get()) {
    218     std::vector<gfx::ImagePNGRep> image_reps;
    219     image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), 1.0f));
    220     image = gfx::Image(image_reps);
    221   }
    222   if (!layer_delegate_)
    223     layer_delegate_.reset(new ImageLayerDelegate());
    224   layer_delegate_->SetImage(image);
    225 
    226   ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
    227   layer->set_delegate(layer_delegate_.get());
    228   return layer;
    229 }
    230 
    231 ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
    232   if (!web_contents_->GetController().CanGoBack())
    233     return NULL;
    234   slide_direction_ = SLIDE_BACK;
    235   return CreateSlideLayer(-1);
    236 }
    237 
    238 ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
    239   if (!web_contents_->GetController().CanGoForward())
    240     return NULL;
    241   slide_direction_ = SLIDE_FRONT;
    242   return CreateSlideLayer(1);
    243 }
    244 
    245 void OverscrollNavigationOverlay::OnWindowSlideCompleting() {
    246   if (slide_direction_ == SLIDE_UNKNOWN)
    247     return;
    248 
    249   // Perform the navigation.
    250   if (slide_direction_ == SLIDE_BACK)
    251     web_contents_->GetController().GoBack();
    252   else if (slide_direction_ == SLIDE_FRONT)
    253     web_contents_->GetController().GoForward();
    254   else
    255     NOTREACHED();
    256 
    257   // Reset state and wait for the new navigation page to complete
    258   // loading/painting.
    259   StartObserving();
    260 }
    261 
    262 void OverscrollNavigationOverlay::OnWindowSlideCompleted(
    263     scoped_ptr<ui::Layer> layer) {
    264   if (slide_direction_ == SLIDE_UNKNOWN) {
    265     window_slider_.reset();
    266     StopObservingIfDone();
    267     return;
    268   }
    269 
    270   // Change the image used for the overlay window.
    271   image_delegate_->SetImage(layer_delegate_->image());
    272   window_->layer()->SetTransform(gfx::Transform());
    273   window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
    274   slide_direction_ = SLIDE_UNKNOWN;
    275   // We may end up dismissing the overlay before it has a chance to repaint, so
    276   // set the slider layer to be the one animated by OverlayDismissAnimator.
    277   if (layer.get())
    278     overlay_dismiss_layer_ = layer.Pass();
    279   StopObservingIfDone();
    280 }
    281 
    282 void OverscrollNavigationOverlay::OnWindowSlideAborted() {
    283   StopObservingIfDone();
    284 }
    285 
    286 void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
    287   // We only want to take an action here if WindowSlider is being destroyed
    288   // outside of OverscrollNavigationOverlay. If window_slider_.get() is NULL,
    289   // then OverscrollNavigationOverlay is the one destroying WindowSlider, and
    290   // we don't need to do anything.
    291   // This check prevents StopObservingIfDone() being called multiple times
    292   // (including recursively) for a single event.
    293   if (window_slider_.get()) {
    294     // The slider has just been destroyed. Release the ownership.
    295     WindowSlider* slider ALLOW_UNUSED = window_slider_.release();
    296     StopObservingIfDone();
    297   }
    298 }
    299 
    300 void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint() {
    301   NavigationEntry* visible_entry =
    302       web_contents_->GetController().GetVisibleEntry();
    303   if (pending_entry_url_.is_empty() ||
    304       DoesEntryMatchURL(visible_entry, pending_entry_url_)) {
    305     received_paint_update_ = true;
    306     StopObservingIfDone();
    307   }
    308 }
    309 
    310 void OverscrollNavigationOverlay::DidStopLoading(RenderViewHost* host) {
    311   // Don't compare URLs in this case - it's possible they won't match if
    312   // a gesture-nav initiated navigation was interrupted by some other in-site
    313   // navigation ((e.g., from a script, or from a bookmark).
    314   loading_complete_ = true;
    315   StopObservingIfDone();
    316 }
    317 
    318 }  // namespace content
    319