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 | |
36 | namespace WebCore { |
37 | |
38 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderLayerModelObject); |
39 | |
40 | bool RenderLayerModelObject::s_wasFloating = false; |
41 | bool RenderLayerModelObject::s_hadLayer = false; |
42 | bool RenderLayerModelObject::s_hadTransform = false; |
43 | bool RenderLayerModelObject::s_layerWasSelfPainting = false; |
44 | |
45 | typedef WTF::HashMap<const RenderLayerModelObject*, RepaintLayoutRects> RepaintLayoutRectsMap; |
46 | static RepaintLayoutRectsMap* gRepaintLayoutRectsMap = nullptr; |
47 | |
48 | RepaintLayoutRects::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 | |
54 | RenderLayerModelObject::RenderLayerModelObject(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags) |
55 | : RenderElement(element, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag) |
56 | { |
57 | } |
58 | |
59 | RenderLayerModelObject::RenderLayerModelObject(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags) |
60 | : RenderElement(document, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag) |
61 | { |
62 | } |
63 | |
64 | RenderLayerModelObject::~RenderLayerModelObject() |
65 | { |
66 | // Do not add any code here. Add it to willBeDestroyed() instead. |
67 | } |
68 | |
69 | void 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 | |
86 | void RenderLayerModelObject::destroyLayer() |
87 | { |
88 | ASSERT(!hasLayer()); |
89 | ASSERT(m_layer); |
90 | if (m_layer->isSelfPaintingLayer()) |
91 | clearRepaintLayoutRects(); |
92 | m_layer = nullptr; |
93 | } |
94 | |
95 | void RenderLayerModelObject::createLayer() |
96 | { |
97 | ASSERT(!m_layer); |
98 | m_layer = std::make_unique<RenderLayer>(*this); |
99 | setHasLayer(true); |
100 | m_layer->insertOnlyThisLayer(); |
101 | } |
102 | |
103 | bool RenderLayerModelObject::hasSelfPaintingLayer() const |
104 | { |
105 | return m_layer && m_layer->isSelfPaintingLayer(); |
106 | } |
107 | |
108 | void 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) |
156 | static bool scrollSnapContainerRequiresUpdateForStyleUpdate(const RenderStyle& oldStyle, const RenderStyle& newStyle) |
157 | { |
158 | return oldStyle.scrollSnapPort() != newStyle.scrollSnapPort(); |
159 | } |
160 | #endif |
161 | |
162 | void 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 | |
237 | bool 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 | |
254 | bool RenderLayerModelObject::hasRepaintLayoutRects() const |
255 | { |
256 | return gRepaintLayoutRectsMap && gRepaintLayoutRectsMap->contains(this); |
257 | } |
258 | |
259 | void RenderLayerModelObject::setRepaintLayoutRects(const RepaintLayoutRects& rects) |
260 | { |
261 | if (!gRepaintLayoutRectsMap) |
262 | gRepaintLayoutRectsMap = new RepaintLayoutRectsMap(); |
263 | gRepaintLayoutRectsMap->set(this, rects); |
264 | } |
265 | |
266 | void RenderLayerModelObject::clearRepaintLayoutRects() |
267 | { |
268 | if (gRepaintLayoutRectsMap) |
269 | gRepaintLayoutRectsMap->remove(this); |
270 | } |
271 | |
272 | RepaintLayoutRects RenderLayerModelObject::repaintLayoutRects() const |
273 | { |
274 | if (!hasRepaintLayoutRects()) |
275 | return RepaintLayoutRects(); |
276 | return gRepaintLayoutRectsMap->get(this); |
277 | } |
278 | |
279 | void 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 | |
287 | bool 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 | |
294 | void RenderLayerModelObject::transitionPaused(double timeOffset, CSSPropertyID propertyId) |
295 | { |
296 | if (!layer() || !layer()->backing()) |
297 | return; |
298 | layer()->backing()->transitionPaused(timeOffset, propertyId); |
299 | } |
300 | |
301 | void RenderLayerModelObject::transitionFinished(CSSPropertyID propertyId) |
302 | { |
303 | if (!layer() || !layer()->backing()) |
304 | return; |
305 | layer()->backing()->transitionFinished(propertyId); |
306 | } |
307 | |
308 | bool 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 | |
315 | void RenderLayerModelObject::animationPaused(double timeOffset, const String& name) |
316 | { |
317 | if (!layer() || !layer()->backing()) |
318 | return; |
319 | layer()->backing()->animationPaused(timeOffset, name); |
320 | } |
321 | |
322 | void RenderLayerModelObject::animationSeeked(double timeOffset, const String& name) |
323 | { |
324 | if (!layer() || !layer()->backing()) |
325 | return; |
326 | layer()->backing()->animationSeeked(timeOffset, name); |
327 | } |
328 | |
329 | void RenderLayerModelObject::animationFinished(const String& name) |
330 | { |
331 | if (!layer() || !layer()->backing()) |
332 | return; |
333 | layer()->backing()->animationFinished(name); |
334 | } |
335 | |
336 | void RenderLayerModelObject::suspendAnimations(MonotonicTime time) |
337 | { |
338 | if (!layer() || !layer()->backing()) |
339 | return; |
340 | layer()->backing()->suspendAnimations(time); |
341 | } |
342 | |
343 | } // namespace WebCore |
344 | |
345 | |