overscroll_navigation_overlay.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "content/browser/web_contents/aura/overscroll_navigation_overlay.h"
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/frame_host/navigation_entry_impl.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/renderer_host/render_view_host_impl.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/web_contents/aura/image_window_delegate.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/web_contents/web_contents_impl.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/common/view_messages.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_widget_host_view.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window.h"
14a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "ui/compositor/layer.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/compositor/layer_animation_observer.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/compositor/scoped_layer_animation_settings.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_png_rep.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace content {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// A LayerDelegate that paints an image for the layer.
24class ImageLayerDelegate : public ui::LayerDelegate {
25 public:
26  ImageLayerDelegate() {}
27
28  virtual ~ImageLayerDelegate() {}
29
30  void SetImage(const gfx::Image& image) {
31    image_ = image;
32    image_size_ = image.AsImageSkia().size();
33  }
34  const gfx::Image& image() const { return image_; }
35
36 private:
37  // Overridden from ui::LayerDelegate:
38  virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
39    if (image_.IsEmpty()) {
40      canvas->DrawColor(SK_ColorGRAY);
41    } else {
42      SkISize size = canvas->sk_canvas()->getDeviceSize();
43      if (size.width() != image_size_.width() ||
44          size.height() != image_size_.height()) {
45        canvas->DrawColor(SK_ColorWHITE);
46      }
47      canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
48    }
49  }
50
51  // Called when the layer's device scale factor has changed.
52  virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
53  }
54
55  // Invoked prior to the bounds changing. The returned closured is run after
56  // the bounds change.
57  virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
58    return base::Closure();
59  }
60
61  gfx::Image image_;
62  gfx::Size image_size_;
63
64  DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
65};
66
67// Responsible for fading out and deleting the layer of the overlay window.
68class OverlayDismissAnimator
69    : public ui::LayerAnimationObserver {
70 public:
71  // Takes ownership of the layer.
72  explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer)
73      : layer_(layer.Pass()) {
74    CHECK(layer_.get());
75  }
76
77  // Starts the fadeout animation on the layer. When the animation finishes,
78  // the object deletes itself along with the layer.
79  void Animate() {
80    DCHECK(layer_.get());
81    ui::LayerAnimator* animator = layer_->GetAnimator();
82    // This makes SetOpacity() animate with default duration (which could be
83    // zero, e.g. when running tests).
84    ui::ScopedLayerAnimationSettings settings(animator);
85    animator->AddObserver(this);
86    layer_->SetOpacity(0);
87  }
88
89  // Overridden from ui::LayerAnimationObserver
90  virtual void OnLayerAnimationEnded(
91      ui::LayerAnimationSequence* sequence) OVERRIDE {
92    delete this;
93  }
94
95  virtual void OnLayerAnimationAborted(
96      ui::LayerAnimationSequence* sequence) OVERRIDE {
97    delete this;
98  }
99
100  virtual void OnLayerAnimationScheduled(
101      ui::LayerAnimationSequence* sequence) OVERRIDE {}
102
103 private:
104  virtual ~OverlayDismissAnimator() {}
105
106  scoped_ptr<ui::Layer> layer_;
107
108  DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator);
109};
110
111OverscrollNavigationOverlay::OverscrollNavigationOverlay(
112    WebContentsImpl* web_contents)
113    : web_contents_(web_contents),
114      image_delegate_(NULL),
115      loading_complete_(false),
116      received_paint_update_(false),
117      pending_entry_id_(0),
118      slide_direction_(SLIDE_UNKNOWN),
119      need_paint_update_(true) {
120}
121
122OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
123}
124
125void OverscrollNavigationOverlay::StartObserving() {
126  loading_complete_ = false;
127  received_paint_update_ = false;
128  pending_entry_id_ = 0;
129  Observe(web_contents_);
130
131  // Make sure the overlay window is on top.
132  if (window_.get() && window_->parent())
133    window_->parent()->StackChildAtTop(window_.get());
134
135  // Assumes the navigation has been initiated.
136  NavigationEntry* pending_entry =
137      web_contents_->GetController().GetPendingEntry();
138  // Save id of the pending entry to identify when it loads and paints later.
139  // Under some circumstances navigation can leave a null pending entry -
140  // see comments in NavigationControllerImpl::NavigateToPendingEntry().
141  pending_entry_id_ = pending_entry ? pending_entry->GetUniqueID() : 0;
142}
143
144void OverscrollNavigationOverlay::SetOverlayWindow(
145    scoped_ptr<aura::Window> window,
146    ImageWindowDelegate* delegate) {
147  window_ = window.Pass();
148  if (window_.get() && window_->parent())
149    window_->parent()->StackChildAtTop(window_.get());
150  image_delegate_ = delegate;
151
152  if (window_.get() && delegate->has_image()) {
153    window_slider_.reset(new WindowSlider(this,
154                                          window_->parent(),
155                                          window_.get()));
156    slide_direction_ = SLIDE_UNKNOWN;
157  } else {
158    window_slider_.reset();
159  }
160}
161
162void OverscrollNavigationOverlay::SetupForTesting() {
163  need_paint_update_ = false;
164}
165
166void OverscrollNavigationOverlay::StopObservingIfDone() {
167  if ((need_paint_update_ && !received_paint_update_)) {
168    return;
169  }
170
171  // If a slide is in progress, then do not destroy the window or the slide.
172  if (window_slider_.get() && window_slider_->IsSlideInProgress())
173    return;
174
175  scoped_ptr<ui::Layer> layer;
176  if (window_.get()) {
177    layer.reset(window_->AcquireLayer());
178  }
179  Observe(NULL);
180  window_slider_.reset();
181  window_.reset();
182  image_delegate_ = NULL;
183  if (layer.get()) {
184    // OverlayDismissAnimator deletes the layer and itself when the animation
185    // completes.
186    (new OverlayDismissAnimator(layer.Pass()))->Animate();
187  }
188}
189
190ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
191  const NavigationControllerImpl& controller = web_contents_->GetController();
192  const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
193      controller.GetEntryAtOffset(offset));
194
195  gfx::Image image;
196  if (entry && entry->screenshot().get()) {
197    std::vector<gfx::ImagePNGRep> image_reps;
198    image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
199        ui::GetImageScale(
200            ui::GetScaleFactorForNativeView(window_.get()))));
201    image = gfx::Image(image_reps);
202  }
203  if (!layer_delegate_)
204    layer_delegate_.reset(new ImageLayerDelegate());
205  layer_delegate_->SetImage(image);
206
207  ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
208  layer->set_delegate(layer_delegate_.get());
209  return layer;
210}
211
212void OverscrollNavigationOverlay::OnUpdateRect(
213    const ViewHostMsg_UpdateRect_Params& params) {
214  if (loading_complete_ &&
215      ViewHostMsg_UpdateRect_Flags::is_repaint_ack(params.flags)) {
216    NavigationEntry* visible_entry =
217        web_contents_->GetController().GetVisibleEntry();
218    int visible_entry_id = visible_entry ? visible_entry->GetUniqueID() : 0;
219    if (visible_entry_id == pending_entry_id_ || !pending_entry_id_) {
220      // This is a paint update after the page has been loaded. So do not wait
221      // for a 'first non-empty' paint update.
222      received_paint_update_ = true;
223      StopObservingIfDone();
224    }
225  }
226}
227
228ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
229  if (!web_contents_->GetController().CanGoBack())
230    return NULL;
231  slide_direction_ = SLIDE_BACK;
232  return CreateSlideLayer(-1);
233}
234
235ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
236  if (!web_contents_->GetController().CanGoForward())
237    return NULL;
238  slide_direction_ = SLIDE_FRONT;
239  return CreateSlideLayer(1);
240}
241
242void OverscrollNavigationOverlay::OnWindowSlideCompleting() {
243  if (slide_direction_ == SLIDE_UNKNOWN)
244    return;
245
246  // Perform the navigation.
247  if (slide_direction_ == SLIDE_BACK)
248    web_contents_->GetController().GoBack();
249  else if (slide_direction_ == SLIDE_FRONT)
250    web_contents_->GetController().GoForward();
251  else
252    NOTREACHED();
253
254  // Reset state and wait for the new navigation page to complete
255  // loading/painting.
256  StartObserving();
257}
258
259void OverscrollNavigationOverlay::OnWindowSlideCompleted() {
260  if (slide_direction_ == SLIDE_UNKNOWN) {
261    window_slider_.reset();
262    StopObservingIfDone();
263    return;
264  }
265
266  // Change the image used for the overlay window.
267  image_delegate_->SetImage(layer_delegate_->image());
268  window_->layer()->SetTransform(gfx::Transform());
269  window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
270  slide_direction_ = SLIDE_UNKNOWN;
271
272  // Make sure the overlay layer is repainted before we dismiss it, otherwise
273  // OverlayDismissAnimator may end up showing the wrong screenshot during the
274  // fadeout animation.
275  if (received_paint_update_ && need_paint_update_) {
276    received_paint_update_ = false;
277    RenderWidgetHost* host =
278        web_contents_->GetRenderWidgetHostView()->GetRenderWidgetHost();
279    RenderViewHostImpl* view_host =
280        static_cast<RenderViewHostImpl*> (RenderViewHost::From(host));
281    view_host->ScheduleComposite();
282  } else if (!need_paint_update_) {
283    StopObservingIfDone();
284  }
285}
286
287void OverscrollNavigationOverlay::OnWindowSlideAborted() {
288  StopObservingIfDone();
289}
290
291void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
292  // We only want to take an action here if WindowSlider is being destroyed
293  // outside of OverscrollNavigationOverlay. If window_slider_.get() is NULL,
294  // then OverscrollNavigationOverlay is the one destroying WindowSlider, and
295  // we don't need to do anything.
296  // This check prevents StopObservingIfDone() being called multiple times
297  // (including recursively) for a single event.
298  if (window_slider_.get()) {
299    // The slider has just been destroyed. Release the ownership.
300    WindowSlider* slider ALLOW_UNUSED = window_slider_.release();
301    StopObservingIfDone();
302  }
303}
304
305void OverscrollNavigationOverlay::DocumentOnLoadCompletedInMainFrame(
306    int32 page_id) {
307  // Use the last committed entry rather than the active one, in case a
308  // pending entry has been created.
309  int committed_entry_id =
310      web_contents_->GetController().GetLastCommittedEntry()->GetUniqueID();
311  // Consider the loading completed once the main frame has loaded.
312  if (committed_entry_id == pending_entry_id_ || !pending_entry_id_) {
313    loading_complete_ = true;
314    StopObservingIfDone();
315  }
316}
317
318void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint(int32 page_id) {
319  int visible_entry_id =
320      web_contents_->GetController().GetVisibleEntry()->GetUniqueID();
321  if (visible_entry_id == pending_entry_id_ || !pending_entry_id_) {
322    received_paint_update_ = true;
323    StopObservingIfDone();
324  }
325}
326
327void OverscrollNavigationOverlay::DidStopLoading(RenderViewHost* host) {
328  // Use the last committed entry rather than the active one, in case a
329  // pending entry has been created.
330  int committed_entry_id =
331      web_contents_->GetController().GetLastCommittedEntry()->GetUniqueID();
332  if (committed_entry_id == pending_entry_id_ || !pending_entry_id_) {
333    loading_complete_ = true;
334    if (!received_paint_update_ && need_paint_update_) {
335      // Force a repaint after the page is loaded.
336      RenderViewHostImpl* view = static_cast<RenderViewHostImpl*>(host);
337      view->ScheduleComposite();
338    }
339    StopObservingIfDone();
340  }
341}
342
343bool OverscrollNavigationOverlay::OnMessageReceived(
344    const IPC::Message& message) {
345  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346  IPC_BEGIN_MESSAGE_MAP(OverscrollNavigationOverlay, message)
347    IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect)
348  IPC_END_MESSAGE_MAP()
349  return false;
350}
351
352}  // namespace content
353