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 "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"
20 // A LayerDelegate that paints an image for the layer.
21 class ImageLayerDelegate : public ui::LayerDelegate {
23 ImageLayerDelegate() {}
25 virtual ~ImageLayerDelegate() {}
27 void SetImage(const gfx::Image& image) {
29 image_size_ = image.AsImageSkia().size();
31 const gfx::Image& image() const { return image_; }
34 // Overridden from ui::LayerDelegate:
35 virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
36 if (image_.IsEmpty()) {
37 canvas->DrawColor(SK_ColorGRAY);
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);
44 canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
48 // Called when the layer's device scale factor has changed.
49 virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
52 // Invoked prior to the bounds changing. The returned closured is run after
54 virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
55 return base::Closure();
59 gfx::Size image_size_;
61 DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
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) {
74 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
77 void OverscrollNavigationOverlay::StartObserving() {
78 loading_complete_ = false;
79 received_paint_update_ = false;
80 Observe(web_contents_);
82 // Make sure the overlay window is on top.
83 if (window_.get() && window_->parent())
84 window_->parent()->StackChildAtTop(window_.get());
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;
95 if (window_.get() && delegate->has_image()) {
96 window_slider_.reset(new WindowSlider(this,
99 slide_direction_ = SLIDE_UNKNOWN;
101 window_slider_.reset();
105 void OverscrollNavigationOverlay::SetupForTesting() {
106 need_paint_update_ = false;
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_)) {
120 // If a slide is in progress, then do not destroy the window or the slide.
121 if (window_slider_.get() && window_slider_->IsSlideInProgress())
125 window_slider_.reset();
127 image_delegate_ = NULL;
130 ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
131 const NavigationControllerImpl& controller = web_contents_->GetController();
132 const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
133 controller.GetEntryAtOffset(offset));
136 if (entry && entry->screenshot().get()) {
137 std::vector<gfx::ImagePNGRep> image_reps;
138 image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
140 ui::GetScaleFactorForNativeView(window_.get()))));
141 image = gfx::Image(image_reps);
143 if (!layer_delegate_)
144 layer_delegate_.reset(new ImageLayerDelegate());
145 layer_delegate_->SetImage(image);
147 ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
148 layer->set_delegate(layer_delegate_.get());
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();
163 ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
164 if (!web_contents_->GetController().CanGoBack())
166 slide_direction_ = SLIDE_BACK;
167 return CreateSlideLayer(-1);
170 ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
171 if (!web_contents_->GetController().CanGoForward())
173 slide_direction_ = SLIDE_FRONT;
174 return CreateSlideLayer(1);
177 void OverscrollNavigationOverlay::OnWindowSlideComplete() {
178 if (slide_direction_ == SLIDE_UNKNOWN) {
179 window_slider_.reset();
180 StopObservingIfDone();
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()));
189 SlideDirection direction = slide_direction_;
190 slide_direction_ = SLIDE_UNKNOWN;
192 // Reset state and wait for the new navigation page to complete
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();
205 void OverscrollNavigationOverlay::OnWindowSlideAborted() {
206 StopObservingIfDone();
209 void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
210 // The slider has just been destroyed. Release the ownership.
211 WindowSlider* slider ALLOW_UNUSED = window_slider_.release();
212 StopObservingIfDone();
215 void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint(int32 page_id) {
216 received_paint_update_ = true;
217 StopObservingIfDone();
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();
227 StopObservingIfDone();
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()
239 } // namespace content