1/*
2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "MessageReceiver.h"
29#include "SameDocumentNavigationType.h"
30#include <WebCore/Color.h>
31#include <WebCore/FloatRect.h>
32#include <WebCore/PageIdentifier.h>
33#include <wtf/MonotonicTime.h>
34#include <wtf/RetainPtr.h>
35#include <wtf/RunLoop.h>
36#include <wtf/WeakPtr.h>
37
38#if PLATFORM(COCOA)
39#include <wtf/BlockPtr.h>
40#endif
41
42#if PLATFORM(GTK)
43#include <WebCore/CairoUtilities.h>
44#include <gtk/gtk.h>
45#endif
46
47#if PLATFORM(COCOA)
48OBJC_CLASS CALayer;
49
50#if PLATFORM(IOS_FAMILY)
51OBJC_CLASS UIGestureRecognizer;
52OBJC_CLASS UIView;
53OBJC_CLASS WKSwipeTransitionController;
54OBJC_CLASS _UINavigationInteractiveTransitionBase;
55OBJC_CLASS _UIViewControllerOneToOneTransitionContext;
56OBJC_CLASS _UIViewControllerTransitionContext;
57#else
58OBJC_CLASS CAGradientLayer;
59OBJC_CLASS NSEvent;
60OBJC_CLASS NSView;
61OBJC_CLASS WKSwipeCancellationTracker;
62#endif
63
64namespace WebCore {
65class IOSurface;
66}
67#endif
68
69#if PLATFORM(MAC)
70typedef NSEvent* PlatformScrollEvent;
71#elif PLATFORM(GTK)
72typedef struct _GdkEventScroll GdkEventScroll;
73typedef GdkEventScroll* PlatformScrollEvent;
74#endif
75
76namespace WebKit {
77
78class ViewSnapshot;
79class WebBackForwardListItem;
80class WebPageProxy;
81class WebProcessProxy;
82
83class ViewGestureController : private IPC::MessageReceiver {
84 WTF_MAKE_FAST_ALLOCATED;
85 WTF_MAKE_NONCOPYABLE(ViewGestureController);
86public:
87 ViewGestureController(WebPageProxy&);
88 ~ViewGestureController();
89 void platformTeardown();
90
91 void disconnectFromProcess();
92 void connectToProcess();
93
94 enum class ViewGestureType {
95 None,
96#if PLATFORM(MAC)
97 Magnification,
98 SmartMagnification,
99#endif
100 Swipe
101 };
102
103 enum class SwipeDirection {
104 Back,
105 Forward
106 };
107
108 typedef uint64_t GestureID;
109
110#if !PLATFORM(IOS_FAMILY)
111 bool handleScrollWheelEvent(PlatformScrollEvent);
112 void wheelEventWasNotHandledByWebCore(PlatformScrollEvent event) { m_pendingSwipeTracker.eventWasNotHandledByWebCore(event); }
113
114 bool shouldIgnorePinnedState() { return m_pendingSwipeTracker.shouldIgnorePinnedState(); }
115 void setShouldIgnorePinnedState(bool ignore) { m_pendingSwipeTracker.setShouldIgnorePinnedState(ignore); }
116
117 bool isPhysicallySwipingLeft(SwipeDirection) const;
118#endif
119
120#if PLATFORM(MAC)
121 double magnification() const;
122
123 void handleMagnificationGestureEvent(PlatformScrollEvent, WebCore::FloatPoint origin);
124
125 bool hasActiveMagnificationGesture() const { return m_activeGestureType == ViewGestureType::Magnification; }
126
127 void handleSmartMagnificationGesture(WebCore::FloatPoint origin);
128
129 void gestureEventWasNotHandledByWebCore(PlatformScrollEvent, WebCore::FloatPoint origin);
130
131 void setCustomSwipeViews(Vector<RetainPtr<NSView>> views) { m_customSwipeViews = WTFMove(views); }
132 void setCustomSwipeViewsTopContentInset(float topContentInset) { m_customSwipeViewsTopContentInset = topContentInset; }
133 WebCore::FloatRect windowRelativeBoundsForCustomSwipeViews() const;
134 void setDidMoveSwipeSnapshotCallback(BlockPtr<void (CGRect)>&& callback) { m_didMoveSwipeSnapshotCallback = WTFMove(callback); }
135#elif PLATFORM(IOS_FAMILY)
136 bool isNavigationSwipeGestureRecognizer(UIGestureRecognizer *) const;
137 void installSwipeHandler(UIView *gestureRecognizerView, UIView *swipingView);
138 void beginSwipeGesture(_UINavigationInteractiveTransitionBase *, SwipeDirection);
139 void endSwipeGesture(WebBackForwardListItem* targetItem, _UIViewControllerTransitionContext *, bool cancelled);
140 void willCommitPostSwipeTransitionLayerTree(bool);
141 void setRenderTreeSize(uint64_t);
142#endif
143
144 void setAlternateBackForwardListSourcePage(WebPageProxy*);
145
146 bool canSwipeInDirection(SwipeDirection) const;
147
148 WebCore::Color backgroundColorForCurrentSnapshot() const { return m_backgroundColorForCurrentSnapshot; }
149
150 void didStartProvisionalLoadForMainFrame();
151 void didFinishLoadForMainFrame() { didReachMainFrameLoadTerminalState(); }
152 void didFailLoadForMainFrame() { didReachMainFrameLoadTerminalState(); }
153 void didFirstVisuallyNonEmptyLayoutForMainFrame();
154 void didRepaintAfterNavigation();
155 void didHitRenderTreeSizeThreshold();
156 void didRestoreScrollPosition();
157 void didReachMainFrameLoadTerminalState();
158 void didSameDocumentNavigationForMainFrame(SameDocumentNavigationType);
159
160 void checkForActiveLoads();
161
162 void removeSwipeSnapshot();
163
164 void setSwipeGestureEnabled(bool enabled) { m_swipeGestureEnabled = enabled; }
165 bool isSwipeGestureEnabled() { return m_swipeGestureEnabled; }
166
167#if PLATFORM(GTK)
168 void draw(cairo_t*, cairo_pattern_t*);
169#endif
170
171 // Testing
172 bool beginSimulatedSwipeInDirectionForTesting(SwipeDirection);
173 bool completeSimulatedSwipeInDirectionForTesting(SwipeDirection);
174
175private:
176 // IPC::MessageReceiver.
177 void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
178
179 static ViewGestureController* controllerForGesture(WebCore::PageIdentifier, GestureID);
180
181 static GestureID takeNextGestureID();
182 void willBeginGesture(ViewGestureType);
183 void didEndGesture();
184
185 void didStartProvisionalOrSameDocumentLoadForMainFrame();
186
187 class SnapshotRemovalTracker {
188 public:
189 enum Event : uint8_t {
190 VisuallyNonEmptyLayout = 1 << 0,
191 RenderTreeSizeThreshold = 1 << 1,
192 RepaintAfterNavigation = 1 << 2,
193 MainFrameLoad = 1 << 3,
194 SubresourceLoads = 1 << 4,
195 ScrollPositionRestoration = 1 << 5
196 };
197 typedef uint8_t Events;
198
199 SnapshotRemovalTracker();
200
201 void start(Events, WTF::Function<void()>&&);
202 void reset();
203
204 void pause() { m_paused = true; }
205 void resume();
206 bool isPaused() const { return m_paused; }
207 bool hasRemovalCallback() const { return !!m_removalCallback; }
208
209 bool eventOccurred(Events);
210 bool cancelOutstandingEvent(Events);
211 bool hasOutstandingEvent(Event);
212
213 void startWatchdog(Seconds);
214
215 uint64_t renderTreeSizeThreshold() const { return m_renderTreeSizeThreshold; }
216 void setRenderTreeSizeThreshold(uint64_t threshold) { m_renderTreeSizeThreshold = threshold; }
217
218 private:
219 static String eventsDescription(Events);
220 void log(const String&) const;
221
222 void fireRemovalCallbackImmediately();
223 void fireRemovalCallbackIfPossible();
224 void watchdogTimerFired();
225
226 bool stopWaitingForEvent(Events, const String& logReason);
227
228 Events m_outstandingEvents { 0 };
229 WTF::Function<void()> m_removalCallback;
230 MonotonicTime m_startTime;
231
232 uint64_t m_renderTreeSizeThreshold { 0 };
233
234 RunLoop::Timer<SnapshotRemovalTracker> m_watchdogTimer;
235
236 bool m_paused { true };
237 };
238
239#if PLATFORM(MAC)
240 // Message handlers.
241 void didCollectGeometryForMagnificationGesture(WebCore::FloatRect visibleContentBounds, bool frameHandlesMagnificationGesture);
242 void didCollectGeometryForSmartMagnificationGesture(WebCore::FloatPoint origin, WebCore::FloatRect renderRect, WebCore::FloatRect visibleContentBounds, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale);
243
244 void endMagnificationGesture();
245
246 WebCore::FloatPoint scaledMagnificationOrigin(WebCore::FloatPoint origin, double scale);
247#endif
248
249#if !PLATFORM(IOS_FAMILY)
250 void startSwipeGesture(PlatformScrollEvent, SwipeDirection);
251 void trackSwipeGesture(PlatformScrollEvent, SwipeDirection, RefPtr<WebBackForwardListItem>);
252
253 void beginSwipeGesture(WebBackForwardListItem* targetItem, SwipeDirection);
254 void handleSwipeGesture(WebBackForwardListItem* targetItem, double progress, SwipeDirection);
255
256 void willEndSwipeGesture(WebBackForwardListItem& targetItem, bool cancelled);
257 void endSwipeGesture(WebBackForwardListItem* targetItem, bool cancelled);
258 bool shouldUseSnapshotForSize(ViewSnapshot&, WebCore::FloatSize swipeLayerSize, float topContentInset);
259
260#if PLATFORM(MAC)
261 CALayer* determineSnapshotLayerParent() const;
262 CALayer* determineLayerAdjacentToSnapshotForParent(SwipeDirection, CALayer* snapshotLayerParent) const;
263 void applyDebuggingPropertiesToSwipeViews();
264 void didMoveSwipeSnapshotLayer();
265#endif
266
267 void requestRenderTreeSizeNotificationIfNeeded();
268 void forceRepaintIfNeeded();
269
270 class PendingSwipeTracker {
271 public:
272 PendingSwipeTracker(WebPageProxy&, ViewGestureController&);
273 bool handleEvent(PlatformScrollEvent);
274 void eventWasNotHandledByWebCore(PlatformScrollEvent);
275
276 void reset(const char* resetReasonForLogging);
277
278 bool shouldIgnorePinnedState() { return m_shouldIgnorePinnedState; }
279 void setShouldIgnorePinnedState(bool ignore) { m_shouldIgnorePinnedState = ignore; }
280
281 private:
282 bool tryToStartSwipe(PlatformScrollEvent);
283 bool scrollEventCanBecomeSwipe(PlatformScrollEvent, SwipeDirection&);
284
285 bool scrollEventCanStartSwipe(PlatformScrollEvent);
286 bool scrollEventCanEndSwipe(PlatformScrollEvent);
287 bool scrollEventCanInfluenceSwipe(PlatformScrollEvent);
288 WebCore::FloatSize scrollEventGetScrollingDeltas(PlatformScrollEvent);
289
290 enum class State {
291 None,
292 WaitingForWebCore,
293 InsufficientMagnitude
294 };
295
296 State m_state { State::None };
297 SwipeDirection m_direction;
298 WebCore::FloatSize m_cumulativeDelta;
299
300 bool m_shouldIgnorePinnedState { false };
301
302 ViewGestureController& m_viewGestureController;
303 WebPageProxy& m_webPageProxy;
304 };
305#endif
306
307 WebPageProxy& m_webPageProxy;
308 ViewGestureType m_activeGestureType { ViewGestureType::None };
309
310 bool m_swipeGestureEnabled { true };
311
312 RunLoop::Timer<ViewGestureController> m_swipeActiveLoadMonitoringTimer;
313
314 WebCore::Color m_backgroundColorForCurrentSnapshot;
315
316 WeakPtr<WebPageProxy> m_alternateBackForwardListSourcePage;
317 RefPtr<WebPageProxy> m_webPageProxyForBackForwardListForCurrentSwipe;
318
319 GestureID m_currentGestureID;
320
321#if !PLATFORM(IOS_FAMILY)
322 RefPtr<ViewSnapshot> m_currentSwipeSnapshot;
323
324 PendingSwipeTracker m_pendingSwipeTracker;
325
326 bool m_hasOutstandingRepaintRequest { false };
327#endif
328
329#if PLATFORM(MAC)
330 double m_magnification;
331 WebCore::FloatPoint m_magnificationOrigin;
332
333 WebCore::FloatRect m_lastSmartMagnificationUnscaledTargetRect;
334 bool m_lastMagnificationGestureWasSmartMagnification { false };
335 WebCore::FloatPoint m_lastSmartMagnificationOrigin;
336
337 WebCore::FloatRect m_visibleContentRect;
338 bool m_visibleContentRectIsValid { false };
339 bool m_frameHandlesMagnificationGesture { false };
340
341 RetainPtr<WKSwipeCancellationTracker> m_swipeCancellationTracker;
342 RetainPtr<CALayer> m_swipeLayer;
343 RetainPtr<CALayer> m_swipeSnapshotLayer;
344 RetainPtr<CAGradientLayer> m_swipeShadowLayer;
345 RetainPtr<CALayer> m_swipeDimmingLayer;
346 Vector<RetainPtr<CALayer>> m_currentSwipeLiveLayers;
347
348 Vector<RetainPtr<NSView>> m_customSwipeViews;
349 float m_customSwipeViewsTopContentInset { 0 };
350 WebCore::FloatRect m_currentSwipeCustomViewBounds;
351
352 BlockPtr<void (CGRect)> m_didMoveSwipeSnapshotCallback;
353#elif PLATFORM(IOS_FAMILY)
354 UIView* m_liveSwipeView { nullptr };
355 RetainPtr<UIView> m_liveSwipeViewClippingView;
356 RetainPtr<UIView> m_snapshotView;
357 RetainPtr<UIView> m_transitionContainerView;
358 RetainPtr<WKSwipeTransitionController> m_swipeInteractiveTransitionDelegate;
359 RetainPtr<_UIViewControllerOneToOneTransitionContext> m_swipeTransitionContext;
360 uint64_t m_snapshotRemovalTargetRenderTreeSize { 0 };
361#endif
362
363#if PLATFORM(GTK)
364 class SwipeProgressTracker {
365 public:
366 SwipeProgressTracker(WebPageProxy&, ViewGestureController&);
367 void startTracking(RefPtr<WebBackForwardListItem>&&, SwipeDirection);
368 void reset();
369 bool handleEvent(PlatformScrollEvent);
370 float progress() const { return m_progress; }
371 SwipeDirection direction() const { return m_direction; }
372
373 private:
374 enum class State {
375 None,
376 Pending,
377 Scrolling,
378 Animating,
379 Finishing
380 };
381
382 bool shouldCancel();
383
384 void startAnimation();
385 gboolean onAnimationTick(GdkFrameClock*);
386 void endAnimation();
387
388 State m_state { State::None };
389
390 SwipeDirection m_direction;
391 RefPtr<WebBackForwardListItem> m_targetItem;
392 unsigned m_tickCallbackID { 0 };
393
394 Seconds m_prevTime;
395 double m_velocity { 0 };
396
397 Seconds m_startTime;
398 Seconds m_endTime;
399
400 float m_progress { 0 };
401 float m_startProgress { 0 };
402 float m_endProgress { 0 };
403 bool m_cancelled { false };
404
405 ViewGestureController& m_viewGestureController;
406 WebPageProxy& m_webPageProxy;
407 };
408
409 SwipeProgressTracker m_swipeProgressTracker;
410
411 RefPtr<cairo_pattern_t> m_currentSwipeSnapshotPattern;
412#endif
413
414 bool m_isConnectedToProcess { false };
415
416 SnapshotRemovalTracker m_snapshotRemovalTracker;
417 WTF::Function<void()> m_loadCallback;
418};
419
420} // namespace WebKit
421