[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / web_contents / aura / overscroll_navigation_overlay.cc
blob14b23752fda79e963e9bff7fde4d370d20193fa4
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 <vector>
9 #include "base/i18n/rtl.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "content/browser/frame_host/navigation_entry_impl.h"
12 #include "content/browser/renderer_host/render_view_host_impl.h"
13 #include "content/browser/web_contents/aura/overscroll_window_delegate.h"
14 #include "content/browser/web_contents/web_contents_impl.h"
15 #include "content/common/view_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_widget_host_view.h"
18 #include "ui/aura/window.h"
19 #include "ui/base/layout.h"
20 #include "ui/compositor/layer.h"
21 #include "ui/compositor/layer_animation_observer.h"
22 #include "ui/compositor/paint_recorder.h"
23 #include "ui/compositor/scoped_layer_animation_settings.h"
24 #include "ui/gfx/canvas.h"
25 #include "ui/gfx/image/image_png_rep.h"
27 namespace content {
28 namespace {
30 // Returns true if the entry's URL or any of the URLs in entry's redirect chain
31 // match |url|.
32 bool DoesEntryMatchURL(NavigationEntry* entry, const GURL& url) {
33 if (!entry)
34 return false;
35 if (entry->GetURL() == url)
36 return true;
37 const std::vector<GURL>& redirect_chain = entry->GetRedirectChain();
38 for (std::vector<GURL>::const_iterator it = redirect_chain.begin();
39 it != redirect_chain.end();
40 it++) {
41 if (*it == url)
42 return true;
44 return false;
47 } // namespace
49 // Responsible for fading out and deleting the layer of the overlay window.
50 class OverlayDismissAnimator
51 : public ui::LayerAnimationObserver {
52 public:
53 // Takes ownership of the layer.
54 explicit OverlayDismissAnimator(scoped_ptr<ui::Layer> layer)
55 : layer_(layer.Pass()) {
56 CHECK(layer_.get());
59 // Starts the fadeout animation on the layer. When the animation finishes,
60 // the object deletes itself along with the layer.
61 void Animate() {
62 DCHECK(layer_.get());
63 ui::LayerAnimator* animator = layer_->GetAnimator();
64 // This makes SetOpacity() animate with default duration (which could be
65 // zero, e.g. when running tests).
66 ui::ScopedLayerAnimationSettings settings(animator);
67 animator->AddObserver(this);
68 layer_->SetOpacity(0);
71 // Overridden from ui::LayerAnimationObserver
72 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
73 delete this;
76 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {
77 delete this;
80 void OnLayerAnimationScheduled(
81 ui::LayerAnimationSequence* sequence) override {}
83 private:
84 ~OverlayDismissAnimator() override {}
86 scoped_ptr<ui::Layer> layer_;
88 DISALLOW_COPY_AND_ASSIGN(OverlayDismissAnimator);
91 OverscrollNavigationOverlay::OverscrollNavigationOverlay(
92 WebContentsImpl* web_contents,
93 aura::Window* web_contents_window)
94 : direction_(NONE),
95 web_contents_(web_contents),
96 loading_complete_(false),
97 received_paint_update_(false),
98 owa_(new OverscrollWindowAnimation(this)),
99 web_contents_window_(web_contents_window) {
102 OverscrollNavigationOverlay::~OverscrollNavigationOverlay() {
103 aura::Window* event_window = GetMainWindow();
104 if (owa_->is_active() && event_window)
105 event_window->ReleaseCapture();
108 void OverscrollNavigationOverlay::StartObserving() {
109 loading_complete_ = false;
110 received_paint_update_ = false;
111 Observe(web_contents_);
113 // Assumes the navigation has been initiated.
114 NavigationEntry* pending_entry =
115 web_contents_->GetController().GetPendingEntry();
116 // Save url of the pending entry to identify when it loads and paints later.
117 // Under some circumstances navigation can leave a null pending entry -
118 // see comments in NavigationControllerImpl::NavigateToPendingEntry().
119 pending_entry_url_ = pending_entry ? pending_entry->GetURL() : GURL();
122 void OverscrollNavigationOverlay::StopObservingIfDone() {
123 // Normally we dismiss the overlay once we receive a paint update, however
124 // for in-page navigations DidFirstVisuallyNonEmptyPaint() does not get
125 // called, and we rely on loading_complete_ for those cases.
126 // If an overscroll gesture is in progress, then do not destroy the window.
127 if (!window_ || !(loading_complete_ || received_paint_update_) ||
128 owa_->is_active()) {
129 return;
132 // OverlayDismissAnimator deletes the dismiss layer and itself when the
133 // animation completes.
134 scoped_ptr<ui::Layer> dismiss_layer = window_->AcquireLayer();
135 window_.reset();
136 (new OverlayDismissAnimator(dismiss_layer.Pass()))->Animate();
137 Observe(nullptr);
138 received_paint_update_ = false;
139 loading_complete_ = false;
142 scoped_ptr<aura::Window> OverscrollNavigationOverlay::CreateOverlayWindow(
143 const gfx::Rect& bounds) {
144 UMA_HISTOGRAM_ENUMERATION(
145 "Overscroll.Started2", direction_, NAVIGATION_COUNT);
146 OverscrollWindowDelegate* overscroll_delegate = new OverscrollWindowDelegate(
147 owa_.get(), GetImageForDirection(direction_));
148 scoped_ptr<aura::Window> window(new aura::Window(overscroll_delegate));
149 window->set_owned_by_parent(false);
150 window->SetTransparent(true);
151 window->Init(ui::LAYER_TEXTURED);
152 window->layer()->SetMasksToBounds(false);
153 window->SetName("OverscrollOverlay");
154 web_contents_window_->AddChild(window.get());
155 aura::Window* event_window = GetMainWindow();
156 if (direction_ == FORWARD)
157 web_contents_window_->StackChildAbove(window.get(), event_window);
158 else
159 web_contents_window_->StackChildBelow(window.get(), event_window);
160 window->SetBounds(bounds);
161 // Set capture on the window that is receiving the overscroll events so that
162 // trackpad scroll gestures keep targetting it even if the mouse pointer moves
163 // off its bounds.
164 event_window->SetCapture();
165 window->Show();
166 return window.Pass();
169 const gfx::Image OverscrollNavigationOverlay::GetImageForDirection(
170 NavigationDirection direction) const {
171 const NavigationControllerImpl& controller = web_contents_->GetController();
172 const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
173 controller.GetEntryAtOffset(direction == FORWARD ? 1 : -1));
175 if (entry && entry->screenshot().get()) {
176 std::vector<gfx::ImagePNGRep> image_reps;
177 image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), 1.0f));
178 return gfx::Image(image_reps);
180 return gfx::Image();
183 scoped_ptr<aura::Window> OverscrollNavigationOverlay::CreateFrontWindow(
184 const gfx::Rect& bounds) {
185 if (!web_contents_->GetController().CanGoForward())
186 return nullptr;
187 direction_ = FORWARD;
188 return CreateOverlayWindow(bounds);
191 scoped_ptr<aura::Window> OverscrollNavigationOverlay::CreateBackWindow(
192 const gfx::Rect& bounds) {
193 if (!web_contents_->GetController().CanGoBack())
194 return nullptr;
195 direction_ = BACK;
196 return CreateOverlayWindow(bounds);
199 aura::Window* OverscrollNavigationOverlay::GetMainWindow() const {
200 if (window_)
201 return window_.get();
202 return web_contents_->IsBeingDestroyed()
203 ? nullptr
204 : web_contents_->GetContentNativeView();
207 void OverscrollNavigationOverlay::OnOverscrollCompleting() {
208 aura::Window* main_window = GetMainWindow();
209 if (!main_window)
210 return;
211 main_window->ReleaseCapture();
214 void OverscrollNavigationOverlay::OnOverscrollCompleted(
215 scoped_ptr<aura::Window> window) {
216 DCHECK(direction_ != NONE);
217 aura::Window* main_window = GetMainWindow();
218 if (!main_window) {
219 UMA_HISTOGRAM_ENUMERATION(
220 "Overscroll.Cancelled", direction_, NAVIGATION_COUNT);
221 return;
224 // Make sure we can navigate first, as other factors can trigger a navigation
225 // during an overscroll gesture and navigating without history produces a
226 // crash.
227 bool navigated = false;
228 if (direction_ == FORWARD && web_contents_->GetController().CanGoForward()) {
229 web_contents_->GetController().GoForward();
230 navigated = true;
231 } else if (direction_ == BACK && web_contents_->GetController().CanGoBack()) {
232 web_contents_->GetController().GoBack();
233 navigated = true;
234 } else {
235 // We need to dismiss the overlay without navigating as soon as the
236 // overscroll finishes.
237 UMA_HISTOGRAM_ENUMERATION(
238 "Overscroll.Cancelled", direction_, NAVIGATION_COUNT);
239 loading_complete_ = true;
242 if (navigated) {
243 UMA_HISTOGRAM_ENUMERATION(
244 "Overscroll.Navigated2", direction_, NAVIGATION_COUNT);
245 StartObserving();
248 main_window->SetTransform(gfx::Transform());
249 window_ = window.Pass();
250 // Make sure the window is in its default position.
251 window_->SetBounds(gfx::Rect(web_contents_window_->bounds().size()));
252 window_->SetTransform(gfx::Transform());
253 // Make sure the overlay window is on top.
254 web_contents_window_->StackChildAtTop(window_.get());
255 direction_ = NONE;
256 StopObservingIfDone();
259 void OverscrollNavigationOverlay::OnOverscrollCancelled() {
260 UMA_HISTOGRAM_ENUMERATION(
261 "Overscroll.Cancelled", direction_, NAVIGATION_COUNT);
262 aura::Window* main_window = GetMainWindow();
263 if (!main_window)
264 return;
265 main_window->ReleaseCapture();
266 direction_ = NONE;
267 StopObservingIfDone();
270 void OverscrollNavigationOverlay::DidFirstVisuallyNonEmptyPaint() {
271 NavigationEntry* visible_entry =
272 web_contents_->GetController().GetVisibleEntry();
273 if (pending_entry_url_.is_empty() ||
274 DoesEntryMatchURL(visible_entry, pending_entry_url_)) {
275 received_paint_update_ = true;
276 StopObservingIfDone();
280 void OverscrollNavigationOverlay::DidStopLoading() {
281 // Don't compare URLs in this case - it's possible they won't match if
282 // a gesture-nav initiated navigation was interrupted by some other in-site
283 // navigation (e.g., from a script, or from a bookmark).
284 loading_complete_ = true;
285 StopObservingIfDone();
288 } // namespace content