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