Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / web_contents / aura / overscroll_navigation_overlay.cc
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