Remove PlatformFile from profile_browsertest
[chromium-blink-merge.git] / content / browser / web_contents / aura / overscroll_navigation_overlay.cc
blobac7b20c3af495bb144a507669e3b605818bfc14f
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/render_widget_host_view.h"
13 #include "ui/aura/window.h"
14 #include "ui/compositor/layer.h"
15 #include "ui/compositor/layer_animation_observer.h"
16 #include "ui/compositor/scoped_layer_animation_settings.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/image/image_png_rep.h"
19 #include "ui/gfx/image/image_skia.h"
21 namespace content {
23 // A LayerDelegate that paints an image for the layer.
24 class ImageLayerDelegate : public ui::LayerDelegate {
25 public:
26 ImageLayerDelegate() {}
28 virtual ~ImageLayerDelegate() {}
30 void SetImage(const gfx::Image& image) {
31 image_ = image;
32 image_size_ = image.AsImageSkia().size();
34 const gfx::Image& image() const { return image_; }
36 private:
37 // Overridden from ui::LayerDelegate:
38 virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
39 if (image_.IsEmpty()) {
40 canvas->DrawColor(SK_ColorGRAY);
41 } else {
42 SkISize size = canvas->sk_canvas()->getDeviceSize();
43 if (size.width() != image_size_.width() ||
44 size.height() != image_size_.height()) {
45 canvas->DrawColor(SK_ColorWHITE);
47 canvas->DrawImageInt(image_.AsImageSkia(), 0, 0);
51 // Called when the layer's device scale factor has changed.
52 virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
55 // Invoked prior to the bounds changing. The returned closured is run after
56 // the bounds change.
57 virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
58 return base::Closure();
61 gfx::Image image_;
62 gfx::Size image_size_;
64 DISALLOW_COPY_AND_ASSIGN(ImageLayerDelegate);
67 // Responsible for fading out and deleting the layer of the overlay window.
68 class OverlayDismissAnimator
69 : public ui::LayerAnimationObserver {
70 public:
71 // Takes ownership of the layer.
72 explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer)
73 : layer_(layer.Pass()) {
74 CHECK(layer_.get());
77 // Starts the fadeout animation on the layer. When the animation finishes,
78 // the object deletes itself along with the layer.
79 void Animate() {
80 DCHECK(layer_.get());
81 ui::LayerAnimator* animator = layer_->GetAnimator();
82 // This makes SetOpacity() animate with default duration (which could be
83 // zero, e.g. when running tests).
84 ui::ScopedLayerAnimationSettings settings(animator);
85 animator->AddObserver(this);
86 layer_->SetOpacity(0);
89 // Overridden from ui::LayerAnimationObserver
90 virtual void OnLayerAnimationEnded(
91 ui::LayerAnimationSequence* sequence) OVERRIDE {
92 delete this;
95 virtual void OnLayerAnimationAborted(
96 ui::LayerAnimationSequence* sequence) OVERRIDE {
97 delete this;
100 virtual void OnLayerAnimationScheduled(
101 ui::LayerAnimationSequence* sequence) OVERRIDE {}
103 private:
104 virtual ~OverlayDismissAnimator() {}
106 scoped_ptr<ui::Layer> layer_;
108 DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator);
111 OverscrollNavigationOverlay::OverscrollNavigationOverlay(
112 WebContentsImpl* web_contents)
113 : web_contents_(web_contents),
114 image_delegate_(NULL),
115 loading_complete_(false),
116 received_paint_update_(false),
117 pending_entry_id_(0),
118 slide_direction_(SLIDE_UNKNOWN),
119 need_paint_update_(true) {
122 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
125 void OverscrollNavigationOverlay::StartObserving() {
126 loading_complete_ = false;
127 received_paint_update_ = false;
128 pending_entry_id_ = 0;
129 Observe(web_contents_);
131 // Make sure the overlay window is on top.
132 if (window_.get() && window_->parent())
133 window_->parent()->StackChildAtTop(window_.get());
136 void OverscrollNavigationOverlay::SetOverlayWindow(
137 scoped_ptr<aura::Window> window,
138 ImageWindowDelegate* delegate) {
139 window_ = window.Pass();
140 if (window_.get() && window_->parent())
141 window_->parent()->StackChildAtTop(window_.get());
142 image_delegate_ = delegate;
144 if (window_.get() && delegate->has_image()) {
145 window_slider_.reset(new WindowSlider(this,
146 window_->parent(),
147 window_.get()));
148 slide_direction_ = SLIDE_UNKNOWN;
149 } else {
150 window_slider_.reset();
154 void OverscrollNavigationOverlay::SetupForTesting() {
155 need_paint_update_ = false;
158 void OverscrollNavigationOverlay::StopObservingIfDone() {
159 // If there is a screenshot displayed in the overlay window, then wait for
160 // the navigated page to complete loading and some paint update before
161 // hiding the overlay.
162 // If there is no screenshot in the overlay window, then hide this view
163 // as soon as there is any new painting notification.
164 if ((need_paint_update_ && !received_paint_update_) ||
165 (image_delegate_->has_image() && !loading_complete_)) {
166 return;
169 // If a slide is in progress, then do not destroy the window or the slide.
170 if (window_slider_.get() && window_slider_->IsSlideInProgress())
171 return;
173 scoped_ptr<ui::Layer> layer;
174 if (window_.get()) {
175 layer.reset(window_->AcquireLayer());
177 Observe(NULL);
178 window_slider_.reset();
179 window_.reset();
180 image_delegate_ = NULL;
181 if (layer.get()) {
182 // OverlayDismissAnimator deletes the layer and itself when the animation
183 // completes.
184 (new OverlayDismissAnimator(layer.Pass()))->Animate();
188 ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
189 const NavigationControllerImpl& controller = web_contents_->GetController();
190 const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
191 controller.GetEntryAtOffset(offset));
193 gfx::Image image;
194 if (entry && entry->screenshot().get()) {
195 std::vector<gfx::ImagePNGRep> image_reps;
196 image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
197 ui::GetImageScale(
198 ui::GetScaleFactorForNativeView(window_.get()))));
199 image = gfx::Image(image_reps);
201 if (!layer_delegate_)
202 layer_delegate_.reset(new ImageLayerDelegate());
203 layer_delegate_->SetImage(image);
205 ui::Layer* layer = new ui::Layer(ui::LAYER_TEXTURED);
206 layer->set_delegate(layer_delegate_.get());
207 return layer;
210 void OverscrollNavigationOverlay::OnUpdateRect(
211 const ViewHostMsg_UpdateRect_Params& params) {
212 if (loading_complete_ &&
213 ViewHostMsg_UpdateRect_Flags::is_repaint_ack(params.flags)) {
214 NavigationEntry* visible_entry =
215 web_contents_->GetController().GetVisibleEntry();
216 int visible_entry_id = visible_entry ? visible_entry->GetUniqueID() : 0;
217 if (visible_entry_id == pending_entry_id_ || !pending_entry_id_) {
218 // This is a paint update after the page has been loaded. So do not wait
219 // for a 'first non-empty' paint update.
220 received_paint_update_ = true;
221 StopObservingIfDone();
226 ui::Layer* OverscrollNavigationOverlay::CreateBackLayer() {
227 if (!web_contents_->GetController().CanGoBack())
228 return NULL;
229 slide_direction_ = SLIDE_BACK;
230 return CreateSlideLayer(-1);
233 ui::Layer* OverscrollNavigationOverlay::CreateFrontLayer() {
234 if (!web_contents_->GetController().CanGoForward())
235 return NULL;
236 slide_direction_ = SLIDE_FRONT;
237 return CreateSlideLayer(1);
240 void OverscrollNavigationOverlay::OnWindowSlideCompleting() {
241 if (slide_direction_ == SLIDE_UNKNOWN)
242 return;
244 // Reset state and wait for the new navigation page to complete
245 // loading/painting.
246 StartObserving();
248 // Perform the navigation.
249 if (slide_direction_ == SLIDE_BACK)
250 web_contents_->GetController().GoBack();
251 else if (slide_direction_ == SLIDE_FRONT)
252 web_contents_->GetController().GoForward();
253 else
254 NOTREACHED();
256 NavigationEntry* pending_entry =
257 web_contents_->GetController().GetPendingEntry();
258 // Save id of the pending entry to identify when it loads and paints later.
259 // Under some circumstances navigation can leave a null pending entry -
260 // see comments in NavigationControllerImpl::NavigateToPendingEntry().
261 pending_entry_id_ = pending_entry ? pending_entry->GetUniqueID() : 0;
264 void OverscrollNavigationOverlay::OnWindowSlideCompleted() {
265 if (slide_direction_ == SLIDE_UNKNOWN) {
266 window_slider_.reset();
267 StopObservingIfDone();
268 return;
271 // Change the image used for the overlay window.
272 image_delegate_->SetImage(layer_delegate_->image());
273 window_->layer()->SetTransform(gfx::Transform());
274 window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
275 slide_direction_ = SLIDE_UNKNOWN;
277 // Make sure the overlay layer is repainted before we dismiss it, otherwise
278 // OverlayDismissAnimator may end up showing the wrong screenshot during the
279 // fadeout animation.
280 if (received_paint_update_ && need_paint_update_) {
281 received_paint_update_ = false;
282 RenderWidgetHost* host =
283 web_contents_->GetRenderWidgetHostView()->GetRenderWidgetHost();
284 RenderViewHostImpl* view_host =
285 static_cast<RenderViewHostImpl*> (RenderViewHost::From(host));
286 view_host->ScheduleComposite();
287 } else if (!need_paint_update_) {
288 StopObservingIfDone();
292 void OverscrollNavigationOverlay::OnWindowSlideAborted() {
293 StopObservingIfDone();
296 void OverscrollNavigationOverlay::OnWindowSliderDestroyed() {
297 // We only want to take an action here if WindowSlider is being destroyed
298 // outside of OverscrollNavigationOverlay. If window_slider_.get() is NULL,
299 // then OverscrollNavigationOverlay is the one destroying WindowSlider, and
300 // we don't need to do anything.
301 // This check prevents StopObservingIfDone() being called multiple times
302 // (including recursively) for a single event.
303 if (window_slider_.get()) {
304 // The slider has just been destroyed. Release the ownership.
305 WindowSlider* slider ALLOW_UNUSED = window_slider_.release();
306 StopObservingIfDone();
310 void OverscrollNavigationOverlay::DocumentOnLoadCompletedInMainFrame(
311 int32 page_id) {
312 // Use the last committed entry rather than the active one, in case a
313 // pending entry has been created.
314 int committed_entry_id =
315 web_contents_->GetController().GetLastCommittedEntry()->GetUniqueID();
316 // For the purposes of dismissing the overlay - consider the loading completed
317 // once the main frame has loaded.
318 if (committed_entry_id == pending_entry_id_ || !pending_entry_id_) {
319 loading_complete_ = true;
320 StopObservingIfDone();
324 void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint(int32 page_id) {
325 int visible_entry_id =
326 web_contents_->GetController().GetVisibleEntry()->GetUniqueID();
327 if (visible_entry_id == pending_entry_id_ || !pending_entry_id_) {
328 received_paint_update_ = true;
329 StopObservingIfDone();
333 void OverscrollNavigationOverlay::DidStopLoading(RenderViewHost* host) {
334 // Use the last committed entry rather than the active one, in case a
335 // pending entry has been created.
336 int committed_entry_id =
337 web_contents_->GetController().GetLastCommittedEntry()->GetUniqueID();
338 if (committed_entry_id == pending_entry_id_ || !pending_entry_id_) {
339 loading_complete_ = true;
340 if (!received_paint_update_) {
341 // Force a repaint after the page is loaded.
342 RenderViewHostImpl* view = static_cast<RenderViewHostImpl*>(host);
343 view->ScheduleComposite();
345 StopObservingIfDone();
349 bool OverscrollNavigationOverlay::OnMessageReceived(
350 const IPC::Message& message) {
351 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352 IPC_BEGIN_MESSAGE_MAP(OverscrollNavigationOverlay, message)
353 IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnUpdateRect)
354 IPC_END_MESSAGE_MAP()
355 return false;
358 } // namespace content