28fe5435769506cf85d5a747aeac96d4d39d44fe
[WebKit-https.git] / Source / WebKit / UIProcess / RemoteLayerTree / ios / RemoteLayerTreeViews.mm
1 /*
2  * Copyright (C) 2014-2018 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 #import "config.h"
27 #import "RemoteLayerTreeViews.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "RemoteLayerTreeHost.h"
32 #import "RemoteLayerTreeNode.h"
33 #import "UIKitSPI.h"
34 #import "WKDrawingView.h"
35 #import <WebCore/Region.h>
36 #import <pal/spi/cocoa/QuartzCoreSPI.h>
37 #import <wtf/SoftLinking.h>
38
39 namespace WebKit {
40
41 static void collectDescendantViewsAtPoint(Vector<UIView *, 16>& viewsAtPoint, UIView *parent, CGPoint point, UIEvent *event)
42 {
43     if (parent.clipsToBounds && ![parent pointInside:point withEvent:event])
44         return;
45
46     for (UIView *view in [parent subviews]) {
47         CGPoint subviewPoint = [view convertPoint:point fromView:parent];
48
49         auto handlesEvent = [&] {
50             if (![view pointInside:subviewPoint withEvent:event])
51                 return false;
52             if (![view isKindOfClass:[WKCompositingView class]])
53                 return true;
54             auto* node = RemoteLayerTreeNode::forCALayer(view.layer);
55             return node->eventRegion().contains(WebCore::IntPoint(subviewPoint));
56         }();
57
58         if (handlesEvent)
59             viewsAtPoint.append(view);
60
61         if (![view subviews])
62             return;
63
64         collectDescendantViewsAtPoint(viewsAtPoint, view, subviewPoint, event);
65     };
66 }
67
68 }
69
70 @interface UIView (WKHitTesting)
71 - (UIView *)_web_findDescendantViewAtPoint:(CGPoint)point withEvent:(UIEvent *)event;
72 @end
73
74 @implementation UIView (WKHitTesting)
75
76 - (UIView *)_web_findDescendantViewAtPoint:(CGPoint)point withEvent:(UIEvent *)event
77 {
78     Vector<UIView *, 16> viewsAtPoint;
79     WebKit::collectDescendantViewsAtPoint(viewsAtPoint, self, point, event);
80
81     for (auto i = viewsAtPoint.size(); i--;) {
82         auto *view = viewsAtPoint[i];
83         if (!view.isUserInteractionEnabled)
84             continue;
85
86         if ([view conformsToProtocol:@protocol(WKNativelyInteractible)]) {
87             CGPoint subviewPoint = [view convertPoint:point fromView:self];
88             return [view hitTest:subviewPoint withEvent:event];
89         }
90
91         if ([view isKindOfClass:[WKChildScrollView class]]) {
92             // See if the deepest view hit is actually a child of the scrollview.
93             if ([viewsAtPoint.last() isDescendantOfView:view])
94                 return view;
95         }
96     }
97     return nil;
98 }
99
100 @end
101
102 @implementation WKCompositingView
103
104 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
105 {
106     return [self _web_findDescendantViewAtPoint:point withEvent:event];
107 }
108
109 - (NSString *)description
110 {
111     return WebKit::RemoteLayerTreeNode::appendLayerDescription(super.description, self.layer);
112 }
113
114 @end
115
116 @implementation WKTransformView
117
118 + (Class)layerClass
119 {
120     return [CATransformLayer self];
121 }
122
123 @end
124
125 @implementation WKSimpleBackdropView
126
127 + (Class)layerClass
128 {
129     return [CABackdropLayer self];
130 }
131
132 @end
133
134 @implementation WKShapeView
135
136 + (Class)layerClass
137 {
138     return [CAShapeLayer self];
139 }
140
141 @end
142
143 @implementation WKRemoteView
144
145 - (instancetype)initWithFrame:(CGRect)frame contextID:(uint32_t)contextID
146 {
147     if ((self = [super initWithFrame:frame])) {
148         CALayerHost *layer = (CALayerHost *)self.layer;
149         layer.contextId = contextID;
150 #if PLATFORM(IOSMAC)
151         // When running iOS apps on macOS, kCAContextIgnoresHitTest isn't respected; instead, we avoid
152         // hit-testing to the remote context by disabling hit-testing on its host layer. See
153         // <rdar://problem/40591107> for more details.
154         layer.allowsHitTesting = NO;
155 #endif
156     }
157     
158     return self;
159 }
160
161 + (Class)layerClass
162 {
163     return NSClassFromString(@"CALayerHost");
164 }
165
166 @end
167
168 #if USE(UIREMOTEVIEW_CONTEXT_HOSTING)
169 @implementation WKUIRemoteView
170
171 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
172 {
173     return [self _web_findDescendantViewAtPoint:point withEvent:event];
174 }
175
176 - (NSString *)description
177 {
178     return WebKit::RemoteLayerTreeNode::appendLayerDescription(super.description, self.layer);
179 }
180
181 @end
182 #endif
183
184 @implementation WKBackdropView
185
186 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
187 {
188     return [self _web_findDescendantViewAtPoint:point withEvent:event];
189 }
190
191 - (NSString *)description
192 {
193     return WebKit::RemoteLayerTreeNode::appendLayerDescription(super.description, self.layer);
194 }
195
196 @end
197
198 @implementation WKChildScrollView
199
200 - (instancetype)initWithFrame:(CGRect)frame
201 {
202     self = [super initWithFrame:frame];
203     if (!self)
204         return nil;
205
206 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
207     self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
208 #endif
209
210     return self;
211 }
212
213 @end
214
215 @implementation WKEmbeddedView
216
217 - (instancetype)initWithEmbeddedViewID:(WebCore::GraphicsLayer::EmbeddedViewID)embeddedViewID
218 {
219     self = [super init];
220     if (!self)
221         return nil;
222
223     _embeddedViewID = embeddedViewID;
224
225     return self;
226 }
227
228 @end
229
230 #endif // PLATFORM(IOS_FAMILY)