1/*
2 * Copyright (C) 2013 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 "RemoteLayerTreePropertyApplier.h"
28
29#import "PlatformCAAnimationRemote.h"
30#import "PlatformCALayerRemote.h"
31#import "RemoteLayerTreeHost.h"
32#import <QuartzCore/CALayer.h>
33#import <WebCore/BlockExceptions.h>
34#import <WebCore/PlatformCAFilters.h>
35#import <WebCore/ScrollbarThemeMac.h>
36
37#if PLATFORM(IOS)
38#import <UIKit/UIView.h>
39#endif
40
41#if __has_include(<QuartzCore/CALayerPrivate.h>)
42#import <QuartzCore/CALayerPrivate.h>
43#endif
44
45@interface CALayer (Details)
46@property BOOL contentsOpaque;
47@end
48
49#if PLATFORM(IOS)
50@interface UIView (WKUIViewUtilities)
51- (void)_web_setSubviews:(NSArray *)subviews;
52@end
53
54@implementation UIView (WKUIViewUtilities)
55
56- (void)_web_setSubviews:(NSArray *)newSubviews
57{
58    NSUInteger numOldSubviews = self.subviews.count;
59    NSUInteger numNewSubviews = newSubviews.count;
60
61    NSUInteger currIndex = 0;
62    for (currIndex = 0; currIndex < numNewSubviews; ++currIndex) {
63        UIView *currNewSubview = [newSubviews objectAtIndex:currIndex];
64
65        if (currIndex < numOldSubviews) {
66            UIView *existingSubview = [self.subviews objectAtIndex:currIndex];
67            if (existingSubview == currNewSubview)
68                continue;
69        }
70
71        // New or moved subview.
72        [self insertSubview:currNewSubview atIndex:currIndex];
73    }
74
75    // Remove views at the end.
76    NSUInteger remainingSubviews = self.subviews.count;
77    for (NSUInteger i = currIndex; i < remainingSubviews; ++i)
78        [[self.subviews objectAtIndex:currIndex] removeFromSuperview];
79
80    ASSERT([self.subviews isEqualToArray:newSubviews]);
81}
82
83@end
84#endif
85
86using namespace WebCore;
87
88namespace WebKit {
89
90static RetainPtr<CGColorRef> cgColorFromColor(Color color)
91{
92    CGFloat components[4];
93    color.getRGBA(components[0], components[1], components[2], components[3]);
94
95    RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
96    return adoptCF(CGColorCreate(colorSpace.get(), components));
97}
98
99static NSString *toCAFilterType(PlatformCALayer::FilterType type)
100{
101    switch (type) {
102    case PlatformCALayer::Linear:
103        return kCAFilterLinear;
104    case PlatformCALayer::Nearest:
105        return kCAFilterNearest;
106    case PlatformCALayer::Trilinear:
107        return kCAFilterTrilinear;
108    };
109
110    ASSERT_NOT_REACHED();
111    return 0;
112}
113
114static void updateCustomAppearance(CALayer *layer, GraphicsLayer::CustomAppearance customAppearance)
115{
116#if ENABLE(RUBBER_BANDING)
117    switch (customAppearance) {
118    case GraphicsLayer::NoCustomAppearance:
119        ScrollbarThemeMac::removeOverhangAreaBackground(layer);
120        ScrollbarThemeMac::removeOverhangAreaShadow(layer);
121        break;
122    case GraphicsLayer::ScrollingOverhang:
123        ScrollbarThemeMac::setUpOverhangAreaBackground(layer);
124        break;
125    case GraphicsLayer::ScrollingShadow:
126        ScrollbarThemeMac::setUpOverhangAreaShadow(layer);
127        break;
128    }
129#else
130    UNUSED_PARAM(customAppearance);
131#endif
132}
133
134static void applyPropertiesToLayer(CALayer *layer, RemoteLayerTreeHost* layerTreeHost, const RemoteLayerTreeTransaction::LayerProperties& properties)
135{
136    if (properties.changedProperties & RemoteLayerTreeTransaction::NameChanged)
137        layer.name = properties.name;
138
139    if (properties.changedProperties & RemoteLayerTreeTransaction::PositionChanged) {
140        layer.position = CGPointMake(properties.position.x(), properties.position.y());
141        layer.zPosition = properties.position.z();
142    }
143
144    if (properties.changedProperties & RemoteLayerTreeTransaction::AnchorPointChanged) {
145        layer.anchorPoint = CGPointMake(properties.anchorPoint.x(), properties.anchorPoint.y());
146        layer.anchorPointZ = properties.anchorPoint.z();
147    }
148
149    if (properties.changedProperties & RemoteLayerTreeTransaction::BoundsChanged)
150        layer.bounds = properties.bounds;
151
152    if (properties.changedProperties & RemoteLayerTreeTransaction::BackgroundColorChanged)
153        layer.backgroundColor = cgColorFromColor(properties.backgroundColor).get();
154
155    if (properties.changedProperties & RemoteLayerTreeTransaction::BorderColorChanged)
156        layer.borderColor = cgColorFromColor(properties.borderColor).get();
157
158    if (properties.changedProperties & RemoteLayerTreeTransaction::BorderWidthChanged)
159        layer.borderWidth = properties.borderWidth;
160
161    if (properties.changedProperties & RemoteLayerTreeTransaction::OpacityChanged)
162        layer.opacity = properties.opacity;
163
164    if (properties.changedProperties & RemoteLayerTreeTransaction::TransformChanged)
165        layer.transform = properties.transform ? (CATransform3D)*properties.transform.get() : CATransform3DIdentity;
166
167    if (properties.changedProperties & RemoteLayerTreeTransaction::SublayerTransformChanged)
168        layer.sublayerTransform = properties.sublayerTransform ? (CATransform3D)*properties.sublayerTransform.get() : CATransform3DIdentity;
169
170    if (properties.changedProperties & RemoteLayerTreeTransaction::HiddenChanged)
171        layer.hidden = properties.hidden;
172
173    if (properties.changedProperties & RemoteLayerTreeTransaction::GeometryFlippedChanged)
174        layer.geometryFlipped = properties.geometryFlipped;
175
176    if (properties.changedProperties & RemoteLayerTreeTransaction::DoubleSidedChanged)
177        layer.doubleSided = properties.doubleSided;
178
179    if (properties.changedProperties & RemoteLayerTreeTransaction::MasksToBoundsChanged)
180        layer.masksToBounds = properties.masksToBounds;
181
182    if (properties.changedProperties & RemoteLayerTreeTransaction::OpaqueChanged)
183        layer.opaque = properties.opaque;
184
185    if (properties.changedProperties & RemoteLayerTreeTransaction::ContentsRectChanged)
186        layer.contentsRect = properties.contentsRect;
187
188    if (properties.changedProperties & RemoteLayerTreeTransaction::ContentsScaleChanged) {
189        layer.contentsScale = properties.contentsScale;
190        layer.rasterizationScale = properties.contentsScale;
191    }
192
193    if (properties.changedProperties & RemoteLayerTreeTransaction::MinificationFilterChanged)
194        layer.minificationFilter = toCAFilterType(properties.minificationFilter);
195
196    if (properties.changedProperties & RemoteLayerTreeTransaction::MagnificationFilterChanged)
197        layer.magnificationFilter = toCAFilterType(properties.magnificationFilter);
198
199    if (properties.changedProperties & RemoteLayerTreeTransaction::BlendModeChanged)
200        PlatformCAFilters::setBlendingFiltersOnLayer(layer, properties.blendMode);
201
202    if (properties.changedProperties & RemoteLayerTreeTransaction::SpeedChanged)
203        layer.speed = properties.speed;
204
205    if (properties.changedProperties & RemoteLayerTreeTransaction::TimeOffsetChanged)
206        layer.timeOffset = properties.timeOffset;
207
208    if (properties.changedProperties & RemoteLayerTreeTransaction::BackingStoreChanged) {
209        if (RemoteLayerBackingStore* backingStore = properties.backingStore.get())
210            backingStore->applyBackingStoreToLayer(layer);
211        else {
212            layer.contents = nil;
213            layer.contentsOpaque = NO;
214        }
215    }
216
217    if (properties.changedProperties & RemoteLayerTreeTransaction::FiltersChanged)
218        PlatformCAFilters::setFiltersOnLayer(layer, properties.filters ? *properties.filters : FilterOperations());
219
220    if (properties.changedProperties & RemoteLayerTreeTransaction::AnimationsChanged)
221        PlatformCAAnimationRemote::updateLayerAnimations(layer, layerTreeHost, properties.addedAnimations, properties.keyPathsOfAnimationsToRemove);
222
223    if (properties.changedProperties & RemoteLayerTreeTransaction::EdgeAntialiasingMaskChanged)
224        layer.edgeAntialiasingMask = properties.edgeAntialiasingMask;
225
226    if (properties.changedProperties & RemoteLayerTreeTransaction::CustomAppearanceChanged)
227        updateCustomAppearance(layer, properties.customAppearance);
228}
229
230void RemoteLayerTreePropertyApplier::applyProperties(CALayer *layer, RemoteLayerTreeHost* layerTreeHost, const RemoteLayerTreeTransaction::LayerProperties& properties, const RelatedLayerMap& relatedLayers)
231{
232    BEGIN_BLOCK_OBJC_EXCEPTIONS;
233    applyPropertiesToLayer(layer, layerTreeHost, properties);
234
235    if (properties.changedProperties & RemoteLayerTreeTransaction::ChildrenChanged) {
236        RetainPtr<NSMutableArray> children = adoptNS([[NSMutableArray alloc] initWithCapacity:properties.children.size()]);
237        for (auto& child : properties.children) {
238            ASSERT(relatedLayers.get(child));
239            [children addObject:relatedLayers.get(child)];
240        }
241
242        layer.sublayers = children.get();
243    }
244
245    if (properties.changedProperties & RemoteLayerTreeTransaction::MaskLayerChanged) {
246        if (!properties.maskLayerID)
247            layer.mask = nullptr;
248        else {
249#if PLATFORM(IOS)
250            UIView *maskView = relatedLayers.get(properties.maskLayerID);
251            // FIXME: need to check that the mask view is kept alive.
252            ASSERT(!maskView.layer.superlayer);
253            if (!maskView.layer.superlayer)
254                layer.mask = maskView.layer;
255#else
256            CALayer *maskLayer = relatedLayers.get(properties.maskLayerID);
257            ASSERT(!maskLayer.superlayer);
258            if (!maskLayer.superlayer)
259                layer.mask = maskLayer;
260#endif
261        }
262    }
263    END_BLOCK_OBJC_EXCEPTIONS;
264}
265
266#if PLATFORM(IOS)
267void RemoteLayerTreePropertyApplier::applyProperties(UIView *view, RemoteLayerTreeHost* layerTreeHost, const RemoteLayerTreeTransaction::LayerProperties& properties, const RelatedLayerMap& relatedLayers)
268{
269    BEGIN_BLOCK_OBJC_EXCEPTIONS;
270    applyPropertiesToLayer(view.layer, layerTreeHost, properties);
271
272    if (properties.changedProperties & RemoteLayerTreeTransaction::ChildrenChanged) {
273        RetainPtr<NSMutableArray> children = adoptNS([[NSMutableArray alloc] initWithCapacity:properties.children.size()]);
274        for (auto& child : properties.children) {
275            ASSERT(relatedLayers.get(child));
276            [children addObject:relatedLayers.get(child)];
277        }
278
279        [view _web_setSubviews:children.get()];
280    }
281
282    if (properties.changedProperties & RemoteLayerTreeTransaction::MaskLayerChanged) {
283        if (!properties.maskLayerID)
284            view.layer.mask = nullptr;
285        else {
286            UIView *maskView = relatedLayers.get(properties.maskLayerID);
287            // FIXME: need to check that the mask view is kept alive.
288            ASSERT(!maskView.layer.superlayer);
289            if (!maskView.layer.superlayer)
290                view.layer.mask = maskView.layer;
291        }
292    }
293    END_BLOCK_OBJC_EXCEPTIONS;
294}
295#endif
296
297} // namespace WebKit
298