1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
7 * Copyright (C) 2010, 2012 Google Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "RenderLayerModelObject.h"
27
28#include "RenderLayer.h"
29#include "RenderLayerBacking.h"
30#include "RenderLayerCompositor.h"
31#include "RenderView.h"
32#include "Settings.h"
33#include "StyleScrollSnapPoints.h"
34#include <wtf/IsoMallocInlines.h>
35
36namespace WebCore {
37
38WTF_MAKE_ISO_ALLOCATED_IMPL(RenderLayerModelObject);
39
40bool RenderLayerModelObject::s_wasFloating = false;
41bool RenderLayerModelObject::s_hadLayer = false;
42bool RenderLayerModelObject::s_hadTransform = false;
43bool RenderLayerModelObject::s_layerWasSelfPainting = false;
44
45typedef WTF::HashMap<const RenderLayerModelObject*, RepaintLayoutRects> RepaintLayoutRectsMap;
46static RepaintLayoutRectsMap* gRepaintLayoutRectsMap = nullptr;
47
48RepaintLayoutRects::RepaintLayoutRects(const RenderLayerModelObject& renderer, const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap)
49 : m_repaintRect(renderer.clippedOverflowRectForRepaint(repaintContainer))
50 , m_outlineBox(renderer.outlineBoundsForRepaint(repaintContainer, geometryMap))
51{
52}
53
54RenderLayerModelObject::RenderLayerModelObject(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
55 : RenderElement(element, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag)
56{
57}
58
59RenderLayerModelObject::RenderLayerModelObject(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
60 : RenderElement(document, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag)
61{
62}
63
64RenderLayerModelObject::~RenderLayerModelObject()
65{
66 // Do not add any code here. Add it to willBeDestroyed() instead.
67}
68
69void RenderLayerModelObject::willBeDestroyed()
70{
71 if (isPositioned()) {
72 if (style().hasViewportConstrainedPosition())
73 view().frameView().removeViewportConstrainedObject(this);
74 }
75
76 if (hasLayer()) {
77 setHasLayer(false);
78 destroyLayer();
79 }
80
81 RenderElement::willBeDestroyed();
82
83 clearRepaintLayoutRects();
84}
85
86void RenderLayerModelObject::destroyLayer()
87{
88 ASSERT(!hasLayer());
89 ASSERT(m_layer);
90 if (m_layer->isSelfPaintingLayer())
91 clearRepaintLayoutRects();
92 m_layer = nullptr;
93}
94
95void RenderLayerModelObject::createLayer()
96{
97 ASSERT(!m_layer);
98 m_layer = std::make_unique<RenderLayer>(*this);
99 setHasLayer(true);
100 m_layer->insertOnlyThisLayer();
101}
102
103bool RenderLayerModelObject::hasSelfPaintingLayer() const
104{
105 return m_layer && m_layer->isSelfPaintingLayer();
106}
107
108void RenderLayerModelObject::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
109{
110 s_wasFloating = isFloating();
111 s_hadLayer = hasLayer();
112 s_hadTransform = hasTransform();
113 if (s_hadLayer)
114 s_layerWasSelfPainting = layer()->isSelfPaintingLayer();
115
116 // If our z-index changes value or our visibility changes,
117 // we need to dirty our stacking context's z-order list.
118 const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr;
119 if (oldStyle) {
120 if (parent()) {
121 // Do a repaint with the old style first, e.g., for example if we go from
122 // having an outline to not having an outline.
123 if (diff == StyleDifference::RepaintLayer) {
124 layer()->repaintIncludingDescendants();
125 if (!(oldStyle->clip() == newStyle.clip()))
126 layer()->clearClipRectsIncludingDescendants();
127 } else if (diff == StyleDifference::Repaint || newStyle.outlineSize() < oldStyle->outlineSize())
128 repaint();
129 }
130
131 if (diff == StyleDifference::Layout || diff == StyleDifference::SimplifiedLayout) {
132 // When a layout hint happens, we do a repaint of the layer, since the layer could end up being destroyed.
133 if (hasLayer()) {
134 if (oldStyle->position() != newStyle.position()
135 || oldStyle->zIndex() != newStyle.zIndex()
136 || oldStyle->hasAutoZIndex() != newStyle.hasAutoZIndex()
137 || !(oldStyle->clip() == newStyle.clip())
138 || oldStyle->hasClip() != newStyle.hasClip()
139 || oldStyle->opacity() != newStyle.opacity()
140 || oldStyle->transform() != newStyle.transform()
141 || oldStyle->filter() != newStyle.filter()
142 )
143 layer()->repaintIncludingDescendants();
144 } else if (newStyle.hasTransform() || newStyle.opacity() < 1 || newStyle.hasFilter() || newStyle.hasBackdropFilter()) {
145 // If we don't have a layer yet, but we are going to get one because of transform or opacity,
146 // then we need to repaint the old position of the object.
147 repaint();
148 }
149 }
150 }
151
152 RenderElement::styleWillChange(diff, newStyle);
153}
154
155#if ENABLE(CSS_SCROLL_SNAP)
156static bool scrollSnapContainerRequiresUpdateForStyleUpdate(const RenderStyle& oldStyle, const RenderStyle& newStyle)
157{
158 return oldStyle.scrollSnapPort() != newStyle.scrollSnapPort();
159}
160#endif
161
162void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
163{
164 RenderElement::styleDidChange(diff, oldStyle);
165 updateFromStyle();
166
167 if (requiresLayer()) {
168 if (!layer() && layerCreationAllowedForSubtree()) {
169 if (s_wasFloating && isFloating())
170 setChildNeedsLayout();
171 createLayer();
172 if (parent() && !needsLayout() && containingBlock()) {
173 layer()->setRepaintStatus(NeedsFullRepaint);
174 layer()->updateLayerPositions();
175 }
176 }
177 } else if (layer() && layer()->parent()) {
178#if ENABLE(CSS_COMPOSITING)
179 if (oldStyle->hasBlendMode())
180 layer()->parent()->dirtyAncestorChainHasBlendingDescendants();
181#endif
182 setHasTransformRelatedProperty(false); // All transform-related propeties force layers, so we know we don't have one or the object doesn't support them.
183 setHasReflection(false);
184 // Repaint the about to be destroyed self-painting layer when style change also triggers repaint.
185 if (layer()->isSelfPaintingLayer() && layer()->repaintStatus() == NeedsFullRepaint && hasRepaintLayoutRects())
186 repaintUsingContainer(containerForRepaint(), repaintLayoutRects().m_repaintRect);
187 layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
188 if (s_wasFloating && isFloating())
189 setChildNeedsLayout();
190 if (s_hadTransform)
191 setNeedsLayoutAndPrefWidthsRecalc();
192 }
193
194 if (layer()) {
195 layer()->styleChanged(diff, oldStyle);
196 if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting)
197 setChildNeedsLayout();
198 }
199
200 bool newStyleIsViewportConstrained = style().hasViewportConstrainedPosition();
201 bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportConstrainedPosition();
202 if (newStyleIsViewportConstrained != oldStyleIsViewportConstrained) {
203 if (newStyleIsViewportConstrained && layer())
204 view().frameView().addViewportConstrainedObject(this);
205 else
206 view().frameView().removeViewportConstrainedObject(this);
207 }
208
209#if ENABLE(CSS_SCROLL_SNAP)
210 const RenderStyle& newStyle = style();
211 if (oldStyle && scrollSnapContainerRequiresUpdateForStyleUpdate(*oldStyle, newStyle)) {
212 if (RenderLayer* renderLayer = layer()) {
213 renderLayer->updateSnapOffsets();
214 renderLayer->updateScrollSnapState();
215 } else if (isBody() || isDocumentElementRenderer()) {
216 FrameView& frameView = view().frameView();
217 frameView.updateSnapOffsets();
218 frameView.updateScrollSnapState();
219 frameView.updateScrollingCoordinatorScrollSnapProperties();
220 }
221 }
222 if (oldStyle && oldStyle->scrollSnapArea() != newStyle.scrollSnapArea()) {
223 auto* scrollSnapBox = enclosingScrollableContainerForSnapping();
224 if (scrollSnapBox && scrollSnapBox->layer()) {
225 const RenderStyle& style = scrollSnapBox->style();
226 if (style.scrollSnapType().strictness != ScrollSnapStrictness::None) {
227 scrollSnapBox->layer()->updateSnapOffsets();
228 scrollSnapBox->layer()->updateScrollSnapState();
229 if (scrollSnapBox->isBody() || scrollSnapBox->isDocumentElementRenderer())
230 scrollSnapBox->view().frameView().updateScrollingCoordinatorScrollSnapProperties();
231 }
232 }
233 }
234#endif
235}
236
237bool RenderLayerModelObject::shouldPlaceBlockDirectionScrollbarOnLeft() const
238{
239// RTL Scrollbars require some system support, and this system support does not exist on certain versions of OS X. iOS uses a separate mechanism.
240#if PLATFORM(IOS_FAMILY)
241 return false;
242#else
243 switch (settings().userInterfaceDirectionPolicy()) {
244 case UserInterfaceDirectionPolicy::Content:
245 return style().shouldPlaceBlockDirectionScrollbarOnLeft();
246 case UserInterfaceDirectionPolicy::System:
247 return settings().systemLayoutDirection() == TextDirection::RTL;
248 }
249 ASSERT_NOT_REACHED();
250 return style().shouldPlaceBlockDirectionScrollbarOnLeft();
251#endif
252}
253
254bool RenderLayerModelObject::hasRepaintLayoutRects() const
255{
256 return gRepaintLayoutRectsMap && gRepaintLayoutRectsMap->contains(this);
257}
258
259void RenderLayerModelObject::setRepaintLayoutRects(const RepaintLayoutRects& rects)
260{
261 if (!gRepaintLayoutRectsMap)
262 gRepaintLayoutRectsMap = new RepaintLayoutRectsMap();
263 gRepaintLayoutRectsMap->set(this, rects);
264}
265
266void RenderLayerModelObject::clearRepaintLayoutRects()
267{
268 if (gRepaintLayoutRectsMap)
269 gRepaintLayoutRectsMap->remove(this);
270}
271
272RepaintLayoutRects RenderLayerModelObject::repaintLayoutRects() const
273{
274 if (!hasRepaintLayoutRects())
275 return RepaintLayoutRects();
276 return gRepaintLayoutRectsMap->get(this);
277}
278
279void RenderLayerModelObject::computeRepaintLayoutRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap)
280{
281 if (!m_layer || !m_layer->isSelfPaintingLayer())
282 clearRepaintLayoutRects();
283 else
284 setRepaintLayoutRects(RepaintLayoutRects(*this, repaintContainer, geometryMap));
285}
286
287bool RenderLayerModelObject::startTransition(double timeOffset, CSSPropertyID propertyId, const RenderStyle* fromStyle, const RenderStyle* toStyle)
288{
289 if (!layer() || !layer()->backing())
290 return false;
291 return layer()->backing()->startTransition(timeOffset, propertyId, fromStyle, toStyle);
292}
293
294void RenderLayerModelObject::transitionPaused(double timeOffset, CSSPropertyID propertyId)
295{
296 if (!layer() || !layer()->backing())
297 return;
298 layer()->backing()->transitionPaused(timeOffset, propertyId);
299}
300
301void RenderLayerModelObject::transitionFinished(CSSPropertyID propertyId)
302{
303 if (!layer() || !layer()->backing())
304 return;
305 layer()->backing()->transitionFinished(propertyId);
306}
307
308bool RenderLayerModelObject::startAnimation(double timeOffset, const Animation& animation, const KeyframeList& keyframes)
309{
310 if (!layer() || !layer()->backing())
311 return false;
312 return layer()->backing()->startAnimation(timeOffset, animation, keyframes);
313}
314
315void RenderLayerModelObject::animationPaused(double timeOffset, const String& name)
316{
317 if (!layer() || !layer()->backing())
318 return;
319 layer()->backing()->animationPaused(timeOffset, name);
320}
321
322void RenderLayerModelObject::animationSeeked(double timeOffset, const String& name)
323{
324 if (!layer() || !layer()->backing())
325 return;
326 layer()->backing()->animationSeeked(timeOffset, name);
327}
328
329void RenderLayerModelObject::animationFinished(const String& name)
330{
331 if (!layer() || !layer()->backing())
332 return;
333 layer()->backing()->animationFinished(name);
334}
335
336void RenderLayerModelObject::suspendAnimations(MonotonicTime time)
337{
338 if (!layer() || !layer()->backing())
339 return;
340 layer()->backing()->suspendAnimations(time);
341}
342
343} // namespace WebCore
344
345