1/*
2 * Copyright (C) 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#import "config.h"
27
28#if PLATFORM(IOS)
29
30#import "RemoteLayerTreeHost.h"
31#import <QuartzCore/QuartzCore.h>
32#import <UIKit/UIScrollView.h>
33#import <WebKitSystemInterface.h>
34
35using namespace WebCore;
36
37@interface CALayer(WKLayerInternal)
38- (void)setContextId:(uint32_t)contextID;
39@end
40
41@interface UIView (WKHitTesting)
42- (UIView *)_findDescendantViewAtPoint:(CGPoint)point withEvent:(UIEvent *)event;
43@end
44
45@implementation UIView (WKHitTesting)
46
47// UIView hit testing assumes that views should only hit test subviews that are entirely contained
48// in the view. This is not true of web content.
49// We only want to find UIScrollViews here. Other views are ignored.
50- (UIView *)_recursiveFindDescendantScrollViewAtPoint:(CGPoint)point withEvent:(UIEvent *)event
51{
52    if (self.clipsToBounds && ![self pointInside:point withEvent:event])
53        return nil;
54
55    __block UIView *foundView = nil;
56    [[self subviews] enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
57        CGPoint subviewPoint = [view convertPoint:point fromView:self];
58
59        if ([view pointInside:subviewPoint withEvent:event] && [view isKindOfClass:[UIScrollView class]])
60            foundView = view;
61
62        if (![view subviews])
63            return;
64
65        if (UIView *hitView = [view _recursiveFindDescendantScrollViewAtPoint:subviewPoint withEvent:event])
66            foundView = hitView;
67    }];
68
69    return foundView;
70}
71
72- (UIView *)_findDescendantViewAtPoint:(CGPoint)point withEvent:(UIEvent *)event
73{
74    return [self _recursiveFindDescendantScrollViewAtPoint:point withEvent:event];
75}
76
77@end
78
79@interface WKCompositingView : UIView
80@end
81
82@implementation WKCompositingView
83- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
84{
85    return [self _findDescendantViewAtPoint:point withEvent:event];
86}
87
88- (NSString *)description
89{
90    NSString *viewDescription = [super description];
91    NSString *webKitDetails = [NSString stringWithFormat:@" layerID = %llu \"%@\"", WebKit::RemoteLayerTreeHost::layerID(self.layer), self.layer.name ? self.layer.name : @""];
92    return [viewDescription stringByAppendingString:webKitDetails];
93}
94@end
95
96@interface WKTransformView : WKCompositingView
97@end
98
99@implementation WKTransformView
100+ (Class)layerClass
101{
102    return [CATransformLayer self];
103}
104
105@end
106
107@interface WKRemoteView : WKCompositingView
108@end
109
110@implementation WKRemoteView
111- (instancetype)initWithFrame:(CGRect)frame contextID:(uint32_t)contextID
112{
113    if ((self = [super initWithFrame:frame])) {
114        [[self layer] setContextId:contextID];
115    }
116
117    return self;
118}
119
120+ (Class)layerClass
121{
122    return NSClassFromString(@"CALayerHost");
123}
124
125@end
126
127namespace WebKit {
128
129LayerOrView *RemoteLayerTreeHost::createLayer(const RemoteLayerTreeTransaction::LayerCreationProperties& properties, const RemoteLayerTreeTransaction::LayerProperties* layerProperties)
130{
131    RetainPtr<LayerOrView>& view = m_layers.add(properties.layerID, nullptr).iterator->value;
132
133    ASSERT(!view);
134
135    switch (properties.type) {
136    case PlatformCALayer::LayerTypeLayer:
137    case PlatformCALayer::LayerTypeWebLayer:
138    case PlatformCALayer::LayerTypeRootLayer:
139    case PlatformCALayer::LayerTypeSimpleLayer:
140    case PlatformCALayer::LayerTypeTiledBackingLayer:
141    case PlatformCALayer::LayerTypePageTiledBackingLayer:
142    case PlatformCALayer::LayerTypeTiledBackingTileLayer:
143        if (layerProperties && layerProperties->customBehavior == GraphicsLayer::CustomScrollingBehavior) {
144            if (!m_isDebugLayerTreeHost)
145                view = adoptNS([[UIScrollView alloc] init]);
146            else // The debug indicator parents views under layers, which can cause crashes with UIScrollView.
147                view = adoptNS([[UIView alloc] init]);
148        } else
149            view = adoptNS([[WKCompositingView alloc] init]);
150        break;
151    case PlatformCALayer::LayerTypeTransformLayer:
152        view = adoptNS([[WKTransformView alloc] init]);
153        break;
154    case PlatformCALayer::LayerTypeCustom:
155    case PlatformCALayer::LayerTypeAVPlayerLayer:
156    case PlatformCALayer::LayerTypeWebGLLayer:
157        if (!m_isDebugLayerTreeHost)
158            view = adoptNS([[WKRemoteView alloc] initWithFrame:CGRectZero contextID:properties.hostingContextID]);
159        else
160            view = adoptNS([[WKCompositingView alloc] init]);
161        break;
162    default:
163        ASSERT_NOT_REACHED();
164    }
165
166    setLayerID([view layer], properties.layerID);
167
168    return view.get();
169}
170
171} // namespace WebKit
172
173#endif // PLATFORM(IOS)
174