Update To 11.40.268.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   ~ImageLayerDelegate() override {}
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   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   void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
72
73   // Called when the layer's device scale factor has changed.
74   void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
75
76   // Invoked prior to the bounds changing. The returned closured is run after
77   // the bounds change.
78   base::Closure PrepareForLayerBoundsChange() override {
79     return base::Closure();
80   }
81
82   gfx::Image image_;
83   gfx::Size image_size_;
84
85   DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
86 };
87
88 // Responsible for fading out and deleting the layer of the overlay window.
89 class OverlayDismissAnimator
90     : public ui::LayerAnimationObserver {
91  public:
92   // Takes ownership of the layer.
93   explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer)
94       : layer_(layer.Pass()) {
95     CHECK(layer_.get());
96   }
97
98   // Starts the fadeout animation on the layer. When the animation finishes,
99   // the object deletes itself along with the layer.
100   void Animate() {
101     DCHECK(layer_.get());
102     ui::LayerAnimator* animator = layer_->GetAnimator();
103     // This makes SetOpacity() animate with default duration (which could be
104     // zero, e.g. when running tests).
105     ui::ScopedLayerAnimationSettings settings(animator);
106     animator->AddObserver(this);
107     layer_->SetOpacity(0);
108   }
109
110   // Overridden from ui::LayerAnimationObserver
111   void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
112     delete this;
113   }
114
115   void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {
116     delete this;
117   }
118
119   void OnLayerAnimationScheduled(
120       ui::LayerAnimationSequence* sequence) override {}
121
122  private:
123   ~OverlayDismissAnimator() override {}
124
125   scoped_ptr<ui::Layer> layer_;
126
127   DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator);
128 };
129
130 OverscrollNavigationOverlay::OverscrollNavigationOverlay(
131     WebContentsImpl* web_contents)
132     : web_contents_(web_contents),
133       image_delegate_(NULL),
134       loading_complete_(false),
135       received_paint_update_(false),
136       slide_direction_(SLIDE_UNKNOWN) {
137 }
138
139 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
140 }
141
142 void OverscrollNavigationOverlay::StartObserving() {
143   loading_complete_ = false;
144   received_paint_update_ = false;
145   overlay_dismiss_layer_.reset();
146   Observe(web_contents_);
147
148   // Make sure the overlay window is on top.
149   if (window_.get() && window_->parent())
150     window_->parent()->StackChildAtTop(window_.get());
151
152   // Assumes the navigation has been initiated.
153   NavigationEntry* pending_entry =
154       web_contents_->GetController().GetPendingEntry();
155   // Save url of the pending entry to identify when it loads and paints later.
156   // Under some circumstances navigation can leave a null pending entry -
157   // see comments in NavigationControllerImpl::NavigateToPendingEntry().
158   pending_entry_url_ = pending_entry ? pending_entry->GetURL() : GURL();
159 }
160
161 void OverscrollNavigationOverlay::SetOverlayWindow(
162     scoped_ptr<aura::Window> window,
163     ImageWindowDelegate* delegate) {
164   window_ = window.Pass();
165   if (window_.get() && window_->parent())
166     window_->parent()->StackChildAtTop(window_.get());
167   image_delegate_ = delegate;
168
169   if (window_.get() && delegate->has_image()) {
170     window_slider_.reset(new WindowSlider(this,
171                                           window_->parent(),
172                                           window_.get()));
173     slide_direction_ = SLIDE_UNKNOWN;
174   } else {
175     window_slider_.reset();
176   }
177 }
178
179 void OverscrollNavigationOverlay::StopObservingIfDone() {
180   // Normally we dismiss the overlay once we receive a paint update, however
181   // for in-page navigations DidFirstVisuallyNonEmptyPaint() does not get
182   // called, and we rely on loading_complete_ for those cases.
183   if (!received_paint_update_ && !loading_complete_)
184     return;
185
186   // If a slide is in progress, then do not destroy the window or the slide.
187   if (window_slider_.get() && window_slider_->IsSlideInProgress())
188     return;
189
190   // The layer to be animated by OverlayDismissAnimator
191   scoped_ptr<ui::Layer> overlay_dismiss_layer;
192   if (overlay_dismiss_layer_)
193     overlay_dismiss_layer = overlay_dismiss_layer_.Pass();
194   else if (window_.get())
195     overlay_dismiss_layer = window_->AcquireLayer();
196   Observe(NULL);
197   window_slider_.reset();
198   window_.reset();
199   image_delegate_ = NULL;
200   if (overlay_dismiss_layer.get()) {
201     // OverlayDismissAnimator deletes overlay_dismiss_layer and itself when the
202     // animation completes.
203     (new OverlayDismissAnimator(overlay_dismiss_layer.Pass()))->Animate();
204   }
205 }
206
207 ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
208   const NavigationControllerImpl& controller = web_contents_->GetController();
209   const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
210       controller.GetEntryAtOffset(offset));
211
212   gfx::Image image;
213   if (entry && entry->screenshot().get()) {
214     std::vector<gfx::ImagePNGRep> image_reps;
215     image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), 1.0f));
216     image = gfx::Image(image_reps);
217   }
218   if (!layer_delegate_)
219     layer_delegate_.reset(new ImageLayerDelegate());
220   layer_delegate_->SetImage(image);
221
222   ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
223   layer->set_delegate(layer_delegate_.get());
224   return layer;
225 }
226
227 ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
228   if (!web_contents_->GetController().CanGoBack())
229     return NULL;
230   slide_direction_ = SLIDE_BACK;
231   return CreateSlideLayer(-1);
232 }
233
234 ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
235   if (!web_contents_->GetController().CanGoForward())
236     return NULL;
237   slide_direction_ = SLIDE_FRONT;
238   return CreateSlideLayer(1);
239 }
240
241 void OverscrollNavigationOverlay::OnWindowSlideCompleting() {
242   if (slide_direction_ == SLIDE_UNKNOWN)
243     return;
244
245   // Perform the navigation.
246   if (slide_direction_ == SLIDE_BACK)
247     web_contents_->GetController().GoBack();
248   else if (slide_direction_ == SLIDE_FRONT)
249     web_contents_->GetController().GoForward();
250   else
251     NOTREACHED();
252
253   // Reset state and wait for the new navigation page to complete
254   // loading/painting.
255   StartObserving();
256 }
257
258 void OverscrollNavigationOverlay::OnWindowSlideCompleted(
259     scoped_ptr<ui::Layer> layer) {
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   // We may end up dismissing the overlay before it has a chance to repaint, so
272   // set the slider layer to be the one animated by OverlayDismissAnimator.
273   if (layer.get())
274     overlay_dismiss_layer_ = layer.Pass();
275   StopObservingIfDone();
276 }
277
278 void OverscrollNavigationOverlay::OnWindowSlideAborted() {
279   StopObservingIfDone();
280 }
281
282 void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
283   // We only want to take an action here if WindowSlider is being destroyed
284   // outside of OverscrollNavigationOverlay. If window_slider_.get() is NULL,
285   // then OverscrollNavigationOverlay is the one destroying WindowSlider, and
286   // we don't need to do anything.
287   // This check prevents StopObservingIfDone() being called multiple times
288   // (including recursively) for a single event.
289   if (window_slider_.get()) {
290     // The slider has just been destroyed. Release the ownership.
291     ignore_result(window_slider_.release());
292     StopObservingIfDone();
293   }
294 }
295
296 void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint() {
297   NavigationEntry* visible_entry =
298       web_contents_->GetController().GetVisibleEntry();
299   if (pending_entry_url_.is_empty() ||
300       DoesEntryMatchURL(visible_entry, pending_entry_url_)) {
301     received_paint_update_ = true;
302     StopObservingIfDone();
303   }
304 }
305
306 void OverscrollNavigationOverlay::DidStopLoading(RenderViewHost* host) {
307   // Don't compare URLs in this case - it's possible they won't match if
308   // a gesture-nav initiated navigation was interrupted by some other in-site
309   // navigation ((e.g., from a script, or from a bookmark).
310   loading_complete_ = true;
311   StopObservingIfDone();
312 }
313
314 }  // namespace content