Upstream version 5.34.104.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 "ui/aura/window.h"
13 #include "ui/compositor/layer.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/gfx/image/image_png_rep.h"
16 #include "ui/gfx/image/image_skia.h"
17
18 namespace content {
19
20 // A LayerDelegate that paints an image for the layer.
21 class ImageLayerDelegate : public ui::LayerDelegate {
22  public:
23   ImageLayerDelegate() {}
24
25   virtual ~ImageLayerDelegate() {}
26
27   void SetImage(const gfx::Image& image) {
28     image_ = image;
29     image_size_ = image.AsImageSkia().size();
30   }
31   const gfx::Image& image() const { return image_; }
32
33  private:
34   // Overridden from ui::LayerDelegate:
35   virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
36     if (image_.IsEmpty()) {
37       canvas->DrawColor(SK_ColorGRAY);
38     } else {
39       SkISize size = canvas->sk_canvas()->getDeviceSize();
40       if (size.width() != image_size_.width() ||
41           size.height() != image_size_.height()) {
42         canvas->DrawColor(SK_ColorWHITE);
43       }
44       canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
45     }
46   }
47
48   // Called when the layer's device scale factor has changed.
49   virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
50   }
51
52   // Invoked prior to the bounds changing. The returned closured is run after
53   // the bounds change.
54   virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
55     return base::Closure();
56   }
57
58   gfx::Image image_;
59   gfx::Size image_size_;
60
61   DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
62 };
63
64 OverscrollNavigationOverlay::OverscrollNavigationOverlay(
65     WebContentsImpl* web_contents)
66     : web_contents_(web_contents),
67       image_delegate_(NULL),
68       loading_complete_(false),
69       received_paint_update_(false),
70       slide_direction_(SLIDE_UNKNOWN),
71       need_paint_update_(true) {
72 }
73
74 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
75 }
76
77 void OverscrollNavigationOverlay::StartObserving() {
78   loading_complete_ = false;
79   received_paint_update_ = false;
80   Observe(web_contents_);
81
82   // Make sure the overlay window is on top.
83   if (window_.get() && window_->parent())
84     window_->parent()->StackChildAtTop(window_.get());
85 }
86
87 void OverscrollNavigationOverlay::SetOverlayWindow(
88     scoped_ptr<aura::Window> window,
89     ImageWindowDelegate* delegate) {
90   window_ = window.Pass();
91   if (window_.get() && window_->parent())
92     window_->parent()->StackChildAtTop(window_.get());
93   image_delegate_ = delegate;
94
95   if (window_.get() && delegate->has_image()) {
96     window_slider_.reset(new WindowSlider(this,
97                                           window_->parent(),
98                                           window_.get()));
99     slide_direction_ = SLIDE_UNKNOWN;
100   } else {
101     window_slider_.reset();
102   }
103 }
104
105 void OverscrollNavigationOverlay::SetupForTesting() {
106   need_paint_update_ = false;
107 }
108
109 void OverscrollNavigationOverlay::StopObservingIfDone() {
110   // If there is a screenshot displayed in the overlay window, then wait for
111   // the navigated page to complete loading and some paint update before
112   // hiding the overlay.
113   // If there is no screenshot in the overlay window, then hide this view
114   // as soon as there is any new painting notification.
115   if ((need_paint_update_ && !received_paint_update_) ||
116       (image_delegate_->has_image() && !loading_complete_)) {
117     return;
118   }
119
120   // If a slide is in progress, then do not destroy the window or the slide.
121   if (window_slider_.get() && window_slider_->IsSlideInProgress())
122     return;
123
124   Observe(NULL);
125   window_slider_.reset();
126   window_.reset();
127   image_delegate_ = NULL;
128 }
129
130 ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
131   const NavigationControllerImpl& controller = web_contents_->GetController();
132   const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
133       controller.GetEntryAtOffset(offset));
134
135   gfx::Image image;
136   if (entry && entry->screenshot().get()) {
137     std::vector<gfx::ImagePNGRep> image_reps;
138     image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
139         ui::GetImageScale(
140             ui::GetScaleFactorForNativeView(window_.get()))));
141     image = gfx::Image(image_reps);
142   }
143   if (!layer_delegate_)
144     layer_delegate_.reset(new ImageLayerDelegate());
145   layer_delegate_->SetImage(image);
146
147   ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
148   layer->set_delegate(layer_delegate_.get());
149   return layer;
150 }
151
152 void OverscrollNavigationOverlay::OnUpdateRect(
153     const ViewHostMsg_UpdateRect_Params& params) {
154   if (loading_complete_ &&
155       ViewHostMsg_UpdateRect_Flags::is_repaint_ack(params.flags)) {
156     // This is a paint update after the page has been loaded. So do not wait for
157     // a 'first non-empty' paint update.
158     received_paint_update_ = true;
159     StopObservingIfDone();
160   }
161 }
162
163 ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
164   if (!web_contents_->GetController().CanGoBack())
165     return NULL;
166   slide_direction_ = SLIDE_BACK;
167   return CreateSlideLayer(-1);
168 }
169
170 ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
171   if (!web_contents_->GetController().CanGoForward())
172     return NULL;
173   slide_direction_ = SLIDE_FRONT;
174   return CreateSlideLayer(1);
175 }
176
177 void OverscrollNavigationOverlay::OnWindowSlideComplete() {
178   if (slide_direction_ == SLIDE_UNKNOWN) {
179     window_slider_.reset();
180     StopObservingIfDone();
181     return;
182   }
183
184   // Change the image used for the overlay window.
185   image_delegate_->SetImage(layer_delegate_->image());
186   window_->layer()->SetTransform(gfx::Transform());
187   window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
188
189   SlideDirection direction = slide_direction_;
190   slide_direction_ = SLIDE_UNKNOWN;
191
192   // Reset state and wait for the new navigation page to complete
193   // loading/painting.
194   StartObserving();
195
196   // Perform the navigation.
197   if (direction == SLIDE_BACK)
198     web_contents_->GetController().GoBack();
199   else if (direction == SLIDE_FRONT)
200     web_contents_->GetController().GoForward();
201   else
202     NOTREACHED();
203 }
204
205 void OverscrollNavigationOverlay::OnWindowSlideAborted() {
206   StopObservingIfDone();
207 }
208
209 void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
210   // The slider has just been destroyed. Release the ownership.
211   WindowSlider* slider ALLOW_UNUSED = window_slider_.release();
212   StopObservingIfDone();
213 }
214
215 void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint(int32 page_id) {
216   received_paint_update_ = true;
217   StopObservingIfDone();
218 }
219
220 void OverscrollNavigationOverlay::DidStopLoading(RenderViewHost* host) {
221   loading_complete_ = true;
222   if (!received_paint_update_) {
223     // Force a repaint after the page is loaded.
224     RenderViewHostImpl* view = static_cast<RenderViewHostImpl*>(host);
225     view->ScheduleComposite();
226   }
227   StopObservingIfDone();
228 }
229
230 bool OverscrollNavigationOverlay::OnMessageReceived(
231     const IPC::Message& message) {
232   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233   IPC_BEGIN_MESSAGE_MAP(OverscrollNavigationOverlay, message)
234     IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect)
235   IPC_END_MESSAGE_MAP()
236   return false;
237 }
238
239 }  // namespace content