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/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"
26 // Returns true if the entry's URL or any of the URLs in entry's redirect chain
28 bool DoesEntryMatchURL(NavigationEntry* entry, const GURL& url) {
29 if (entry->GetURL() == url)
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();
43 // A LayerDelegate that paints an image for the layer.
44 class ImageLayerDelegate : public ui::LayerDelegate {
46 ImageLayerDelegate() {}
48 virtual ~ImageLayerDelegate() {}
50 void SetImage(const gfx::Image& image) {
52 image_size_ = image.AsImageSkia().size();
54 const gfx::Image& image() const { return image_; }
57 // Overridden from ui::LayerDelegate:
58 virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
59 if (image_.IsEmpty()) {
60 canvas->DrawColor(SK_ColorWHITE);
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);
67 canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
71 virtual void OnDelegatedFrameDamage(
72 const gfx::Rect& damage_rect_in_dip) OVERRIDE {}
74 // Called when the layer's device scale factor has changed.
75 virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
78 // Invoked prior to the bounds changing. The returned closured is run after
80 virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
81 return base::Closure();
85 gfx::Size image_size_;
87 DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
90 // Responsible for fading out and deleting the layer of the overlay window.
91 class OverlayDismissAnimator
92 : public ui::LayerAnimationObserver {
94 // Takes ownership of the layer.
95 explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer)
96 : layer_(layer.Pass()) {
100 // Starts the fadeout animation on the layer. When the animation finishes,
101 // the object deletes itself along with the layer.
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);
112 // Overridden from ui::LayerAnimationObserver
113 virtual void OnLayerAnimationEnded(
114 ui::LayerAnimationSequence* sequence) OVERRIDE {
118 virtual void OnLayerAnimationAborted(
119 ui::LayerAnimationSequence* sequence) OVERRIDE {
123 virtual void OnLayerAnimationScheduled(
124 ui::LayerAnimationSequence* sequence) OVERRIDE {}
127 virtual ~OverlayDismissAnimator() {}
129 scoped_ptr<ui::Layer> layer_;
131 DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator);
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) {
143 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
146 void OverscrollNavigationOverlay::StartObserving() {
147 loading_complete_ = false;
148 received_paint_update_ = false;
149 overlay_dismiss_layer_.reset();
150 Observe(web_contents_);
152 // Make sure the overlay window is on top.
153 if (window_.get() && window_->parent())
154 window_->parent()->StackChildAtTop(window_.get());
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();
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;
173 if (window_.get() && delegate->has_image()) {
174 window_slider_.reset(new WindowSlider(this,
177 slide_direction_ = SLIDE_UNKNOWN;
179 window_slider_.reset();
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_)
190 // If a slide is in progress, then do not destroy the window or the slide.
191 if (window_slider_.get() && window_slider_->IsSlideInProgress())
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();
201 window_slider_.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();
211 ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
212 const NavigationControllerImpl& controller = web_contents_->GetController();
213 const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
214 controller.GetEntryAtOffset(offset));
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);
222 if (!layer_delegate_)
223 layer_delegate_.reset(new ImageLayerDelegate());
224 layer_delegate_->SetImage(image);
226 ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
227 layer->set_delegate(layer_delegate_.get());
231 ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
232 if (!web_contents_->GetController().CanGoBack())
234 slide_direction_ = SLIDE_BACK;
235 return CreateSlideLayer(-1);
238 ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
239 if (!web_contents_->GetController().CanGoForward())
241 slide_direction_ = SLIDE_FRONT;
242 return CreateSlideLayer(1);
245 void OverscrollNavigationOverlay::OnWindowSlideCompleting() {
246 if (slide_direction_ == SLIDE_UNKNOWN)
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();
257 // Reset state and wait for the new navigation page to complete
262 void OverscrollNavigationOverlay::OnWindowSlideCompleted(
263 scoped_ptr<ui::Layer> layer) {
264 if (slide_direction_ == SLIDE_UNKNOWN) {
265 window_slider_.reset();
266 StopObservingIfDone();
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.
278 overlay_dismiss_layer_ = layer.Pass();
279 StopObservingIfDone();
282 void OverscrollNavigationOverlay::OnWindowSlideAborted() {
283 StopObservingIfDone();
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();
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();
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();
318 } // namespace content