Unreviewed, roll out http://trac.webkit.org/changeset/187972.
[WebKit-https.git] / Source / WebKit / ios / WebCoreSupport / WebFixedPositionContent.mm
1 /*
2  * Copyright (C) 2011 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 #if PLATFORM(IOS)
27
28 #import "WebFixedPositionContent.h"
29 #import "WebFixedPositionContentInternal.h"
30
31 #import "WebViewInternal.h"
32 #import <WebCore/ChromeClient.h>
33 #import <WebCore/CoreGraphicsSPI.h>
34 #import <WebCore/Frame.h>
35 #import <WebCore/IntSize.h>
36 #import <WebCore/ScrollingConstraints.h>
37 #import <WebCore/WebCoreThreadRun.h>
38
39 #import <wtf/HashMap.h>
40 #import <wtf/RetainPtr.h>
41 #import <wtf/StdLibExtras.h>
42 #import <wtf/Threading.h>
43
44 #import <Foundation/Foundation.h>
45 #import <QuartzCore/QuartzCore.h>
46 #import <algorithm>
47
48 using namespace WebCore;
49 using namespace std;
50
51 static Mutex& WebFixedPositionContentDataLock()
52 {
53     DEPRECATED_DEFINE_STATIC_LOCAL(Mutex, mutex, ());
54     return mutex;
55 }
56
57 struct ViewportConstrainedLayerData {
58     ViewportConstrainedLayerData()
59         : m_enclosingAcceleratedScrollLayer(nil)
60     { }
61     CALayer* m_enclosingAcceleratedScrollLayer; // May be nil.
62     std::unique_ptr<ViewportConstraints> m_viewportConstraints;
63 };
64
65 typedef HashMap<RetainPtr<CALayer>, std::unique_ptr<ViewportConstrainedLayerData>> LayerInfoMap;
66
67 struct WebFixedPositionContentData {
68 public:
69     WebFixedPositionContentData(WebView *);
70     ~WebFixedPositionContentData();
71     
72     WebView *m_webView;
73     LayerInfoMap m_viewportConstrainedLayers;
74 };
75
76
77 WebFixedPositionContentData::WebFixedPositionContentData(WebView *webView)
78     : m_webView(webView)
79 {
80 }
81
82 WebFixedPositionContentData::~WebFixedPositionContentData()
83 {
84 }
85
86 @implementation WebFixedPositionContent
87
88 - (id)initWithWebView:(WebView *)webView
89 {
90     if ((self = [super init])) {
91         _private = new WebFixedPositionContentData(webView);
92     }
93     return self;
94 }
95
96 - (void)dealloc
97 {
98     delete _private;
99     [super dealloc];
100 }
101
102 - (void)scrollOrZoomChanged:(CGRect)positionedObjectsRect
103 {
104     MutexLocker lock(WebFixedPositionContentDataLock());
105
106     LayerInfoMap::const_iterator end = _private->m_viewportConstrainedLayers.end();
107     for (LayerInfoMap::const_iterator it = _private->m_viewportConstrainedLayers.begin(); it != end; ++it) {
108         CALayer *layer = it->key.get();
109         ViewportConstrainedLayerData* constraintData = it->value.get();
110         const ViewportConstraints& constraints = *(constraintData->m_viewportConstraints.get());
111
112         switch (constraints.constraintType()) {
113         case ViewportConstraints::FixedPositionConstraint: {
114                 const FixedPositionViewportConstraints& fixedConstraints = static_cast<const FixedPositionViewportConstraints&>(constraints);
115
116                 FloatPoint layerPosition = fixedConstraints.layerPositionForViewportRect(positionedObjectsRect);
117             
118                 CGRect layerBounds = [layer bounds];
119                 CGPoint anchorPoint = [layer anchorPoint];
120                 CGPoint newPosition = CGPointMake(layerPosition.x() - constraints.alignmentOffset().width() + anchorPoint.x * layerBounds.size.width,
121                                                   layerPosition.y() - constraints.alignmentOffset().height() + anchorPoint.y * layerBounds.size.height);
122                 [layer setPosition:newPosition];
123                 break;
124             }
125         case ViewportConstraints::StickyPositionConstraint: {
126                 const StickyPositionViewportConstraints& stickyConstraints = static_cast<const StickyPositionViewportConstraints&>(constraints);
127
128                 FloatPoint layerPosition = stickyConstraints.layerPositionForConstrainingRect(positionedObjectsRect);
129
130                 CGRect layerBounds = [layer bounds];
131                 CGPoint anchorPoint = [layer anchorPoint];
132                 CGPoint newPosition = CGPointMake(layerPosition.x() - constraints.alignmentOffset().width() + anchorPoint.x * layerBounds.size.width,
133                                                   layerPosition.y() - constraints.alignmentOffset().height() + anchorPoint.y * layerBounds.size.height);
134                 [layer setPosition:newPosition];
135                 break;
136             }
137         }
138     }
139 }
140
141 - (void)overflowScrollPositionForLayer:(CALayer *)scrollLayer changedTo:(CGPoint)scrollPosition
142 {
143     MutexLocker lock(WebFixedPositionContentDataLock());
144
145     LayerInfoMap::const_iterator end = _private->m_viewportConstrainedLayers.end();
146     for (LayerInfoMap::const_iterator it = _private->m_viewportConstrainedLayers.begin(); it != end; ++it) {
147         CALayer *layer = it->key.get();
148         ViewportConstrainedLayerData* constraintData = it->value.get();
149         
150         if (constraintData->m_enclosingAcceleratedScrollLayer == scrollLayer) {
151             const StickyPositionViewportConstraints& stickyConstraints = static_cast<const StickyPositionViewportConstraints&>(*(constraintData->m_viewportConstraints.get()));
152             FloatRect constrainingRectAtLastLayout = stickyConstraints.constrainingRectAtLastLayout();
153             FloatRect scrolledConstrainingRect = FloatRect(scrollPosition.x, scrollPosition.y, constrainingRectAtLastLayout.width(), constrainingRectAtLastLayout.height());
154             FloatPoint layerPosition = stickyConstraints.layerPositionForConstrainingRect(scrolledConstrainingRect);
155
156             CGRect layerBounds = [layer bounds];
157             CGPoint anchorPoint = [layer anchorPoint];
158             CGPoint newPosition = CGPointMake(layerPosition.x() - stickyConstraints.alignmentOffset().width() + anchorPoint.x * layerBounds.size.width,
159                                               layerPosition.y() - stickyConstraints.alignmentOffset().height() + anchorPoint.y * layerBounds.size.height);
160             [layer setPosition:newPosition];
161         }
162     }
163 }
164
165 // FIXME: share code with 'sendScrollEvent'?
166 - (void)didFinishScrollingOrZooming
167 {
168     WebThreadRun(^{
169         if (Frame* frame = [_private->m_webView _mainCoreFrame])
170             frame->viewportOffsetChanged(Frame::CompletedScrollOffset);
171     });
172 }
173
174 - (void)setViewportConstrainedLayers:(WTF::HashMap<CALayer *, std::unique_ptr<WebCore::ViewportConstraints>>&)layerMap stickyContainerMap:(WTF::HashMap<CALayer*, CALayer*>&)stickyContainers
175 {
176     MutexLocker lock(WebFixedPositionContentDataLock());
177
178     _private->m_viewportConstrainedLayers.clear();
179
180     for (auto& layerAndConstraints : layerMap) {
181         CALayer* layer = layerAndConstraints.key;
182         auto layerData = std::make_unique<ViewportConstrainedLayerData>();
183
184         layerData->m_enclosingAcceleratedScrollLayer = stickyContainers.get(layer);
185         layerData->m_viewportConstraints = WTF::move(layerAndConstraints.value);
186
187         _private->m_viewportConstrainedLayers.set(layer, WTF::move(layerData));
188     }
189 }
190
191 - (BOOL)hasFixedOrStickyPositionLayers
192 {
193     MutexLocker lock(WebFixedPositionContentDataLock());
194     return !_private->m_viewportConstrainedLayers.isEmpty();
195 }
196
197 static ViewportConstraints::AnchorEdgeFlags anchorEdgeFlagsForAnchorEdge(WebFixedPositionAnchorEdge edge)
198 {
199     switch (edge) {
200     case WebFixedPositionAnchorEdgeLeft:
201         return ViewportConstraints::AnchorEdgeFlags::AnchorEdgeLeft;
202     case WebFixedPositionAnchorEdgeRight:
203         return ViewportConstraints::AnchorEdgeFlags::AnchorEdgeRight;
204     case WebFixedPositionAnchorEdgeTop:
205         return ViewportConstraints::AnchorEdgeFlags::AnchorEdgeTop;
206     case WebFixedPositionAnchorEdgeBottom:
207         return ViewportConstraints::AnchorEdgeFlags::AnchorEdgeBottom;
208     }
209 }
210
211 - (CGFloat)minimumOffsetFromFixedPositionLayersToAnchorEdge:(WebFixedPositionAnchorEdge)anchorEdge ofRect:(CGRect)rect inLayer:(CALayer *)layer
212 {
213     MutexLocker lock(WebFixedPositionContentDataLock());
214     ViewportConstraints::AnchorEdgeFlags anchorEdgeFlags = anchorEdgeFlagsForAnchorEdge(anchorEdge);
215     CGFloat minimumOffset = CGFLOAT_MAX;
216     LayerInfoMap::const_iterator end = _private->m_viewportConstrainedLayers.end();
217     for (LayerInfoMap::const_iterator it = _private->m_viewportConstrainedLayers.begin(); it != end; ++it) {
218         CALayer *fixedLayer = it->key.get();
219         ViewportConstrainedLayerData* constraintData = it->value.get();
220         const ViewportConstraints& constraints = *(constraintData->m_viewportConstraints.get());
221
222         if (!constraints.hasAnchorEdge(anchorEdgeFlags))
223             continue;
224         // According to Simon: It's possible that there are windows of time
225         // where these CALayers are unparented (because we've flushed on the web
226         // thread but haven't updated those layers yet).
227         if (![fixedLayer superlayer])
228             continue;
229
230         CGRect fixedLayerExtent = [layer convertRect:[fixedLayer bounds] fromLayer:fixedLayer];
231         CGFloat offset;
232         switch (anchorEdge) {
233         case WebFixedPositionAnchorEdgeLeft:
234             offset = CGRectGetMinX(fixedLayerExtent) - CGRectGetMinX(rect);
235             break;
236         case WebFixedPositionAnchorEdgeRight:
237             offset = CGRectGetMaxX(rect) - CGRectGetMaxX(fixedLayerExtent);
238             break;
239         case WebFixedPositionAnchorEdgeTop:
240             offset = CGRectGetMinY(fixedLayerExtent) - CGRectGetMinY(rect);
241             break;
242         case WebFixedPositionAnchorEdgeBottom:
243             offset = CGRectGetMaxY(rect) - CGRectGetMaxY(fixedLayerExtent);
244             break;
245         }
246         minimumOffset = CGFloatMin(minimumOffset, offset);
247     }
248     return minimumOffset;
249 }
250
251 @end
252
253 #endif // PLATFORM(IOS)