1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/browser/web_contents/aura/gesture_nav_simple.h"
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "cc/layers/layer.h"
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/browser/frame_host/navigation_controller_impl.h"
9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/browser/renderer_host/overscroll_controller.h"
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/browser/web_contents/web_contents_impl.h"
11010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "content/browser/web_contents/web_contents_view.h"
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/public/browser/overscroll_configuration.h"
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/public/common/content_client.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "grit/ui_resources.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/aura/window.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/compositor/layer.h"
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/compositor/layer_animation_observer.h"
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/compositor/layer_delegate.h"
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/compositor/scoped_layer_animation_settings.h"
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/animation/tween.h"
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/canvas.h"
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/image/image.h"
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace content {
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace {
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kArrowHeight = 280;
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kArrowWidth = 140;
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const float kMinOpacity = 0.25f;
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool ShouldNavigateForward(const NavigationController& controller,
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                           OverscrollMode mode) {
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return mode == (base::i18n::IsRTL() ? OVERSCROLL_EAST : OVERSCROLL_WEST) &&
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         controller.CanGoForward();
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool ShouldNavigateBack(const NavigationController& controller,
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        OverscrollMode mode) {
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return mode == (base::i18n::IsRTL() ? OVERSCROLL_WEST : OVERSCROLL_EAST) &&
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         controller.CanGoBack();
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// An animation observers that deletes itself and a pointer after the end of the
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// animation.
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)template <class T>
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class DeleteAfterAnimation : public ui::ImplicitAnimationObserver {
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public:
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  explicit DeleteAfterAnimation(scoped_ptr<T> object)
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      : object_(object.Pass()) {}
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private:
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  friend class base::DeleteHelper<DeleteAfterAnimation<T> >;
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual ~DeleteAfterAnimation() {}
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // ui::ImplicitAnimationObserver:
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual void OnImplicitAnimationsCompleted() OVERRIDE {
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Deleting an observer when a ScopedLayerAnimationSettings is iterating
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // over them can cause a crash (which can happen during tests). So instead,
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // schedule this observer to be deleted soon.
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  scoped_ptr<T> object_;
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(DeleteAfterAnimation);
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// A layer delegate that paints the shield with the arrow in it.
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class ArrowLayerDelegate : public ui::LayerDelegate {
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public:
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  explicit ArrowLayerDelegate(int resource_id)
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      : image_(GetContentClient()->GetNativeImageNamed(resource_id)),
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        left_arrow_(resource_id == IDR_BACK_ARROW) {
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    CHECK(!image_.IsEmpty());
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual ~ArrowLayerDelegate() {}
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool left() const { return left_arrow_; }
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private:
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // ui::LayerDelegate:
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    SkPaint paint;
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    paint.setColor(SkColorSetARGB(0xa0, 0, 0, 0));
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    paint.setStyle(SkPaint::kFill_Style);
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    paint.setAntiAlias(true);
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    canvas->DrawCircle(
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        gfx::Point(left_arrow_ ? 0 : kArrowWidth, kArrowHeight / 2),
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        kArrowWidth,
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        paint);
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    canvas->DrawImageInt(*image_.ToImageSkia(),
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                         left_arrow_ ? 0 : kArrowWidth - image_.Width(),
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                         (kArrowHeight - image_.Height()) / 2);
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return base::Closure();
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const gfx::Image& image_;
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const bool left_arrow_;
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ArrowLayerDelegate);
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)GestureNavSimple::GestureNavSimple(WebContentsImpl* web_contents)
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : web_contents_(web_contents),
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      completion_threshold_(0.f) {}
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)GestureNavSimple::~GestureNavSimple() {}
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GestureNavSimple::ApplyEffectsAndDestroy(const gfx::Transform& transform,
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                              float opacity) {
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ui::Layer* layer = arrow_.get();
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ui::ScopedLayerAnimationSettings settings(arrow_->GetAnimator());
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  settings.AddObserver(
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      new DeleteAfterAnimation<ArrowLayerDelegate>(arrow_delegate_.Pass()));
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  settings.AddObserver(new DeleteAfterAnimation<ui::Layer>(arrow_.Pass()));
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  settings.AddObserver(new DeleteAfterAnimation<ui::Layer>(clip_layer_.Pass()));
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  layer->SetTransform(transform);
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  layer->SetOpacity(opacity);
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GestureNavSimple::AbortGestureAnimation() {
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!arrow_)
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Transform transform;
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  transform.Translate(arrow_delegate_->left() ? -kArrowWidth : kArrowWidth, 0);
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ApplyEffectsAndDestroy(transform, kMinOpacity);
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GestureNavSimple::CompleteGestureAnimation() {
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!arrow_)
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Make sure the fade-out starts from the complete state.
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ApplyEffectsForDelta(completion_threshold_);
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ApplyEffectsAndDestroy(arrow_->transform(), 0.f);
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GestureNavSimple::ApplyEffectsForDelta(float delta_x) {
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!arrow_)
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK_GT(completion_threshold_, 0.f);
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK_GE(delta_x, 0.f);
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  double complete = std::min(1.f, delta_x / completion_threshold_);
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  float translate_x = gfx::Tween::FloatValueBetween(complete, -kArrowWidth, 0);
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Transform transform;
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  transform.Translate(arrow_delegate_->left() ? translate_x : -translate_x,
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                      0.f);
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  arrow_->SetTransform(transform);
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  arrow_->SetOpacity(gfx::Tween::FloatValueBetween(complete, kMinOpacity, 1.f));
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)gfx::Rect GestureNavSimple::GetVisibleBounds() const {
163010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return web_contents_->GetNativeView()->bounds();
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) {
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ApplyEffectsForDelta(std::abs(delta_x) + 50.f);
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GestureNavSimple::OnOverscrollComplete(OverscrollMode overscroll_mode) {
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CompleteGestureAnimation();
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NavigationControllerImpl& controller = web_contents_->GetController();
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (ShouldNavigateForward(controller, overscroll_mode))
175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    controller.GoForward();
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else if (ShouldNavigateBack(controller, overscroll_mode))
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    controller.GoBack();
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void GestureNavSimple::OnOverscrollModeChange(OverscrollMode old_mode,
181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                              OverscrollMode new_mode) {
182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  NavigationControllerImpl& controller = web_contents_->GetController();
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!ShouldNavigateForward(controller, new_mode) &&
184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      !ShouldNavigateBack(controller, new_mode)) {
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    AbortGestureAnimation();
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  arrow_.reset(new ui::Layer(ui::LAYER_TEXTURED));
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Note that RTL doesn't affect the arrow that should be displayed.
191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int resource_id = 0;
192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (new_mode == OVERSCROLL_WEST)
193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    resource_id = IDR_FORWARD_ARROW;
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else if (new_mode == OVERSCROLL_EAST)
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    resource_id = IDR_BACK_ARROW;
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    NOTREACHED();
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  arrow_delegate_.reset(new ArrowLayerDelegate(resource_id));
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  arrow_->set_delegate(arrow_delegate_.get());
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  arrow_->SetFillsBoundsOpaquely(false);
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
203010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  aura::Window* window = web_contents_->GetNativeView();
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const gfx::Rect& window_bounds = window->bounds();
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  completion_threshold_ = window_bounds.width() *
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Align on the left or right edge.
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int x = (resource_id == IDR_BACK_ARROW) ? 0 :
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      (window_bounds.width() - kArrowWidth);
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Align in the center vertically.
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int y = std::max(0, (window_bounds.height() - kArrowHeight) / 2);
213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  arrow_->SetBounds(gfx::Rect(x, y, kArrowWidth, kArrowHeight));
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ApplyEffectsForDelta(0.f);
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Adding the arrow as a child of the content window is not sufficient,
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // because it is possible for a new layer to be parented on top of the arrow
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // layer (e.g. when the navigated-to page is displayed while the completion
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // animation is in progress). So instead, a clip layer (that doesn't paint) is
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // installed on top of the content window as its sibling, and the arrow layer
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // is added to that clip layer.
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  clip_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN));
223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  clip_layer_->SetBounds(window->layer()->bounds());
224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  clip_layer_->SetMasksToBounds(true);
225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  clip_layer_->Add(arrow_.get());
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ui::Layer* parent = window->layer()->parent();
228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  parent->Add(clip_layer_.get());
229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  parent->StackAtTop(clip_layer_.get());
230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace content
233