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.
5 #include "content/browser/web_contents/aura/overscroll_navigation_overlay.h"
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/render_widget_host_view.h"
13 #include "ui/aura/window.h"
14 #include "ui/compositor/layer.h"
15 #include "ui/compositor/layer_animation_observer.h"
16 #include "ui/compositor/scoped_layer_animation_settings.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/image/image_png_rep.h"
19 #include "ui/gfx/image/image_skia.h"
23 // A LayerDelegate that paints an image for the layer.
24 class ImageLayerDelegate
: public ui::LayerDelegate
{
26 ImageLayerDelegate() {}
28 virtual ~ImageLayerDelegate() {}
30 void SetImage(const gfx::Image
& image
) {
32 image_size_
= image
.AsImageSkia().size();
34 const gfx::Image
& image() const { return image_
; }
37 // Overridden from ui::LayerDelegate:
38 virtual void OnPaintLayer(gfx::Canvas
* canvas
) OVERRIDE
{
39 if (image_
.IsEmpty()) {
40 canvas
->DrawColor(SK_ColorGRAY
);
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
);
47 canvas
->DrawImageInt(image_
.AsImageSkia(), 0, 0);
51 // Called when the layer's device scale factor has changed.
52 virtual void OnDeviceScaleFactorChanged(float device_scale_factor
) OVERRIDE
{
55 // Invoked prior to the bounds changing. The returned closured is run after
57 virtual base::Closure
PrepareForLayerBoundsChange() OVERRIDE
{
58 return base::Closure();
62 gfx::Size image_size_
;
64 DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate
);
67 // Responsible for fading out and deleting the layer of the overlay window.
68 class OverlayDismissAnimator
69 : public ui::LayerAnimationObserver
{
71 // Takes ownership of the layer.
72 explicit OverlayDismissAnimator(scoped_ptr
<ui::Layer
> layer
)
73 : layer_(layer
.Pass()) {
77 // Starts the fadeout animation on the layer. When the animation finishes,
78 // the object deletes itself along with the layer.
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);
89 // Overridden from ui::LayerAnimationObserver
90 virtual void OnLayerAnimationEnded(
91 ui::LayerAnimationSequence
* sequence
) OVERRIDE
{
95 virtual void OnLayerAnimationAborted(
96 ui::LayerAnimationSequence
* sequence
) OVERRIDE
{
100 virtual void OnLayerAnimationScheduled(
101 ui::LayerAnimationSequence
* sequence
) OVERRIDE
{}
104 virtual ~OverlayDismissAnimator() {}
106 scoped_ptr
<ui::Layer
> layer_
;
108 DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator
);
111 OverscrollNavigationOverlay::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) {
122 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
125 void OverscrollNavigationOverlay::StartObserving() {
126 loading_complete_
= false;
127 received_paint_update_
= false;
128 pending_entry_id_
= 0;
129 Observe(web_contents_
);
131 // Make sure the overlay window is on top.
132 if (window_
.get() && window_
->parent())
133 window_
->parent()->StackChildAtTop(window_
.get());
136 void OverscrollNavigationOverlay::SetOverlayWindow(
137 scoped_ptr
<aura::Window
> window
,
138 ImageWindowDelegate
* delegate
) {
139 window_
= window
.Pass();
140 if (window_
.get() && window_
->parent())
141 window_
->parent()->StackChildAtTop(window_
.get());
142 image_delegate_
= delegate
;
144 if (window_
.get() && delegate
->has_image()) {
145 window_slider_
.reset(new WindowSlider(this,
148 slide_direction_
= SLIDE_UNKNOWN
;
150 window_slider_
.reset();
154 void OverscrollNavigationOverlay::SetupForTesting() {
155 need_paint_update_
= false;
158 void OverscrollNavigationOverlay::StopObservingIfDone() {
159 // If there is a screenshot displayed in the overlay window, then wait for
160 // the navigated page to complete loading and some paint update before
161 // hiding the overlay.
162 // If there is no screenshot in the overlay window, then hide this view
163 // as soon as there is any new painting notification.
164 if ((need_paint_update_
&& !received_paint_update_
) ||
165 (image_delegate_
->has_image() && !loading_complete_
)) {
169 // If a slide is in progress, then do not destroy the window or the slide.
170 if (window_slider_
.get() && window_slider_
->IsSlideInProgress())
173 scoped_ptr
<ui::Layer
> layer
;
175 layer
.reset(window_
->AcquireLayer());
178 window_slider_
.reset();
180 image_delegate_
= NULL
;
182 // OverlayDismissAnimator deletes the layer and itself when the animation
184 (new OverlayDismissAnimator(layer
.Pass()))->Animate();
188 ui::Layer
* OverscrollNavigationOverlay::CreateSlideLayer(int offset
) {
189 const NavigationControllerImpl
& controller
= web_contents_
->GetController();
190 const NavigationEntryImpl
* entry
= NavigationEntryImpl::FromNavigationEntry(
191 controller
.GetEntryAtOffset(offset
));
194 if (entry
&& entry
->screenshot().get()) {
195 std::vector
<gfx::ImagePNGRep
> image_reps
;
196 image_reps
.push_back(gfx::ImagePNGRep(entry
->screenshot(),
198 ui::GetScaleFactorForNativeView(window_
.get()))));
199 image
= gfx::Image(image_reps
);
201 if (!layer_delegate_
)
202 layer_delegate_
.reset(new ImageLayerDelegate());
203 layer_delegate_
->SetImage(image
);
205 ui::Layer
* layer
= new ui::Layer(ui::LAYER_TEXTURED
);
206 layer
->set_delegate(layer_delegate_
.get());
210 void OverscrollNavigationOverlay::OnUpdateRect(
211 const ViewHostMsg_UpdateRect_Params
& params
) {
212 if (loading_complete_
&&
213 ViewHostMsg_UpdateRect_Flags::is_repaint_ack(params
.flags
)) {
214 NavigationEntry
* visible_entry
=
215 web_contents_
->GetController().GetVisibleEntry();
216 int visible_entry_id
= visible_entry
? visible_entry
->GetUniqueID() : 0;
217 if (visible_entry_id
== pending_entry_id_
|| !pending_entry_id_
) {
218 // This is a paint update after the page has been loaded. So do not wait
219 // for a 'first non-empty' paint update.
220 received_paint_update_
= true;
221 StopObservingIfDone();
226 ui::Layer
* OverscrollNavigationOverlay::CreateBackLayer() {
227 if (!web_contents_
->GetController().CanGoBack())
229 slide_direction_
= SLIDE_BACK
;
230 return CreateSlideLayer(-1);
233 ui::Layer
* OverscrollNavigationOverlay::CreateFrontLayer() {
234 if (!web_contents_
->GetController().CanGoForward())
236 slide_direction_
= SLIDE_FRONT
;
237 return CreateSlideLayer(1);
240 void OverscrollNavigationOverlay::OnWindowSlideCompleting() {
241 if (slide_direction_
== SLIDE_UNKNOWN
)
244 // Reset state and wait for the new navigation page to complete
248 // Perform the navigation.
249 if (slide_direction_
== SLIDE_BACK
)
250 web_contents_
->GetController().GoBack();
251 else if (slide_direction_
== SLIDE_FRONT
)
252 web_contents_
->GetController().GoForward();
256 NavigationEntry
* pending_entry
=
257 web_contents_
->GetController().GetPendingEntry();
258 // Save id of the pending entry to identify when it loads and paints later.
259 // Under some circumstances navigation can leave a null pending entry -
260 // see comments in NavigationControllerImpl::NavigateToPendingEntry().
261 pending_entry_id_
= pending_entry
? pending_entry
->GetUniqueID() : 0;
264 void OverscrollNavigationOverlay::OnWindowSlideCompleted() {
265 if (slide_direction_
== SLIDE_UNKNOWN
) {
266 window_slider_
.reset();
267 StopObservingIfDone();
271 // Change the image used for the overlay window.
272 image_delegate_
->SetImage(layer_delegate_
->image());
273 window_
->layer()->SetTransform(gfx::Transform());
274 window_
->SchedulePaintInRect(gfx::Rect(window_
->bounds().size()));
275 slide_direction_
= SLIDE_UNKNOWN
;
277 // Make sure the overlay layer is repainted before we dismiss it, otherwise
278 // OverlayDismissAnimator may end up showing the wrong screenshot during the
279 // fadeout animation.
280 if (received_paint_update_
&& need_paint_update_
) {
281 received_paint_update_
= false;
282 RenderWidgetHost
* host
=
283 web_contents_
->GetRenderWidgetHostView()->GetRenderWidgetHost();
284 RenderViewHostImpl
* view_host
=
285 static_cast<RenderViewHostImpl
*> (RenderViewHost::From(host
));
286 view_host
->ScheduleComposite();
287 } else if (!need_paint_update_
) {
288 StopObservingIfDone();
292 void OverscrollNavigationOverlay::OnWindowSlideAborted() {
293 StopObservingIfDone();
296 void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
297 // We only want to take an action here if WindowSlider is being destroyed
298 // outside of OverscrollNavigationOverlay. If window_slider_.get() is NULL,
299 // then OverscrollNavigationOverlay is the one destroying WindowSlider, and
300 // we don't need to do anything.
301 // This check prevents StopObservingIfDone() being called multiple times
302 // (including recursively) for a single event.
303 if (window_slider_
.get()) {
304 // The slider has just been destroyed. Release the ownership.
305 WindowSlider
* slider ALLOW_UNUSED
= window_slider_
.release();
306 StopObservingIfDone();
310 void OverscrollNavigationOverlay::DocumentOnLoadCompletedInMainFrame(
312 // Use the last committed entry rather than the active one, in case a
313 // pending entry has been created.
314 int committed_entry_id
=
315 web_contents_
->GetController().GetLastCommittedEntry()->GetUniqueID();
316 // For the purposes of dismissing the overlay - consider the loading completed
317 // once the main frame has loaded.
318 if (committed_entry_id
== pending_entry_id_
|| !pending_entry_id_
) {
319 loading_complete_
= true;
320 StopObservingIfDone();
324 void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint(int32 page_id
) {
325 int visible_entry_id
=
326 web_contents_
->GetController().GetVisibleEntry()->GetUniqueID();
327 if (visible_entry_id
== pending_entry_id_
|| !pending_entry_id_
) {
328 received_paint_update_
= true;
329 StopObservingIfDone();
333 void OverscrollNavigationOverlay::DidStopLoading(RenderViewHost
* host
) {
334 // Use the last committed entry rather than the active one, in case a
335 // pending entry has been created.
336 int committed_entry_id
=
337 web_contents_
->GetController().GetLastCommittedEntry()->GetUniqueID();
338 if (committed_entry_id
== pending_entry_id_
|| !pending_entry_id_
) {
339 loading_complete_
= true;
340 if (!received_paint_update_
) {
341 // Force a repaint after the page is loaded.
342 RenderViewHostImpl
* view
= static_cast<RenderViewHostImpl
*>(host
);
343 view
->ScheduleComposite();
345 StopObservingIfDone();
349 bool OverscrollNavigationOverlay::OnMessageReceived(
350 const IPC::Message
& message
) {
351 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
352 IPC_BEGIN_MESSAGE_MAP(OverscrollNavigationOverlay
, message
)
353 IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect
, OnUpdateRect
)
354 IPC_END_MESSAGE_MAP()
358 } // namespace content