source: webkit/trunk/Source/WebCore/rendering/RenderLayerModelObject.cpp

Last change on this file was 295665, checked in by Alan Bujtas, 3 years ago

REGRESSION (r294902): Content with continuation leaves decoration bits behind when removed
https://bugs.webkit.org/show_bug.cgi?id=241734
<rdar://95308322>

Reviewed by Simon Fraser.

This patch ensures that when a renderer is removed we always issue a repaint regardless of what the associated layer's repaint bit says.

  1. after r294902, repaint is not issued anymore if either the associated or an ancestor layer have the "full repaint" bit set.
  2. such layer-driven repaints happen after layout.

In some dynamic content cases, the layer may be removed before layout happens. This patch ensures that we preemptively issue such repaints.

  • LayoutTests/fast/repaint/force-repaint-when-layer-is-destroyed-expected.txt: Added.
  • LayoutTests/fast/repaint/force-repaint-when-layer-is-destroyed.html: Added.
  • Source/WebCore/rendering/RenderLayerModelObject.cpp: Force (full) repaint when the renderer is being destroyed (detached -> non-internal move).

(WebCore::RenderLayerModelObject::willBeRemovedFromTree):

  • Source/WebCore/rendering/RenderLayerModelObject.h:
  • Source/WebCore/rendering/RenderObject.cpp: move duplicated code from repaint() and repaintRectangle() to issueRepaint().

Canonical link: https://commits.webkit.org/251670@main

File size: 15.5 KB
Line 
1/*
2 * Copyright (C) 1999 Lars Knoll ([email protected])
3 * (C) 1999 Antti Koivisto ([email protected])
4 * (C) 2005 Allan Sandfeld Jensen ([email protected])
5 * (C) 2005, 2006 Samuel Weinig ([email protected])
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 "RenderLayerScrollableArea.h"
32#include "RenderSVGBlock.h"
33#include "RenderSVGModelObject.h"
34#include "RenderView.h"
35#include "SVGGraphicsElement.h"
36#include "Settings.h"
37#include "StyleScrollSnapPoints.h"
38#include "TransformState.h"
39#include <wtf/IsoMallocInlines.h>
40
41namespace WebCore {
42
43WTF_MAKE_ISO_ALLOCATED_IMPL(RenderLayerModelObject);
44
45bool RenderLayerModelObject::s_wasFloating = false;
46bool RenderLayerModelObject::s_hadLayer = false;
47bool RenderLayerModelObject::s_hadTransform = false;
48bool RenderLayerModelObject::s_layerWasSelfPainting = false;
49
50RenderLayerModelObject::RenderLayerModelObject(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
51 : RenderElement(element, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag)
52{
53}
54
55RenderLayerModelObject::RenderLayerModelObject(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
56 : RenderElement(document, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag)
57{
58}
59
60RenderLayerModelObject::~RenderLayerModelObject()
61{
62 // Do not add any code here. Add it to willBeDestroyed() instead.
63}
64
65void RenderLayerModelObject::willBeDestroyed()
66{
67 if (isPositioned()) {
68 if (style().hasViewportConstrainedPosition())
69 view().frameView().removeViewportConstrainedObject(*this);
70 }
71
72 if (hasLayer()) {
73 setHasLayer(false);
74 destroyLayer();
75 }
76
77 RenderElement::willBeDestroyed();
78}
79
80void RenderLayerModelObject::willBeRemovedFromTree(IsInternalMove isInternalMove)
81{
82 if (auto* layer = this->layer(); layer && layer->needsFullRepaint() && isInternalMove == IsInternalMove::No)
83 issueRepaint(std::nullopt, ClipRepaintToLayer::No, ForceRepaint::Yes);
84
85 RenderElement::willBeRemovedFromTree(isInternalMove);
86}
87
88void RenderLayerModelObject::destroyLayer()
89{
90 ASSERT(!hasLayer());
91 ASSERT(m_layer);
92 m_layer = nullptr;
93}
94
95void RenderLayerModelObject::createLayer()
96{
97 ASSERT(!m_layer);
98 m_layer = makeUnique<RenderLayer>(*this);
99 setHasLayer(true);
100 m_layer->insertOnlyThisLayer(RenderLayer::LayerChangeTiming::StyleChange);
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 auto* oldStyle = hasInitializedStyle() ? &style() : nullptr;
117 if (diff == StyleDifference::RepaintLayer && parent() && oldStyle && oldStyle->clip() != newStyle.clip())
118 layer()->clearClipRectsIncludingDescendants();
119 RenderElement::styleWillChange(diff, newStyle);
120}
121
122void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
123{
124 RenderElement::styleDidChange(diff, oldStyle);
125 updateFromStyle();
126
127 if (requiresLayer()) {
128 if (!layer() && layerCreationAllowedForSubtree()) {
129 if (s_wasFloating && isFloating())
130 setChildNeedsLayout();
131 createLayer();
132 if (parent() && !needsLayout() && containingBlock())
133 layer()->setRepaintStatus(NeedsFullRepaint);
134 }
135 } else if (layer() && layer()->parent()) {
136#if ENABLE(CSS_COMPOSITING)
137 if (oldStyle && oldStyle->hasBlendMode())
138 layer()->willRemoveChildWithBlendMode();
139#endif
140 setHasTransformRelatedProperty(false); // All transform-related properties force layers, so we know we don't have one or the object doesn't support them.
141#if ENABLE(LAYER_BASED_SVG_ENGINE)
142 setHasSVGTransform(false); // Same reason as for setHasTransformRelatedProperty().
143#endif
144 setHasReflection(false);
145
146 // Repaint the about to be destroyed self-painting layer when style change also triggers repaint.
147 if (layer()->isSelfPaintingLayer() && layer()->repaintStatus() == NeedsFullRepaint && layer()->repaintRects())
148 repaintUsingContainer(containerForRepaint().renderer, layer()->repaintRects()->clippedOverflowRect);
149
150 layer()->removeOnlyThisLayer(RenderLayer::LayerChangeTiming::StyleChange); // calls destroyLayer() which clears m_layer
151 if (s_wasFloating && isFloating())
152 setChildNeedsLayout();
153 if (s_hadTransform)
154 setNeedsLayoutAndPrefWidthsRecalc();
155 }
156
157 if (layer()) {
158 layer()->styleChanged(diff, oldStyle);
159 if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting)
160 setChildNeedsLayout();
161 }
162
163 bool newStyleIsViewportConstrained = style().hasViewportConstrainedPosition();
164 bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportConstrainedPosition();
165 if (newStyleIsViewportConstrained != oldStyleIsViewportConstrained) {
166 if (newStyleIsViewportConstrained && layer())
167 view().frameView().addViewportConstrainedObject(*this);
168 else
169 view().frameView().removeViewportConstrainedObject(*this);
170 }
171
172 const RenderStyle& newStyle = style();
173 if (oldStyle && oldStyle->scrollPadding() != newStyle.scrollPadding()) {
174 if (isDocumentElementRenderer()) {
175 FrameView& frameView = view().frameView();
176 frameView.updateScrollbarSteps();
177 } else if (RenderLayer* renderLayer = layer())
178 renderLayer->updateScrollbarSteps();
179 }
180
181 bool scrollMarginChanged = oldStyle && oldStyle->scrollMargin() != newStyle.scrollMargin();
182 bool scrollAlignChanged = oldStyle && oldStyle->scrollSnapAlign() != newStyle.scrollSnapAlign();
183 bool scrollSnapStopChanged = oldStyle && oldStyle->scrollSnapStop() != newStyle.scrollSnapStop();
184 if (scrollMarginChanged || scrollAlignChanged || scrollSnapStopChanged) {
185 if (auto* scrollSnapBox = enclosingScrollableContainerForSnapping())
186 scrollSnapBox->setNeedsLayout();
187 }
188}
189
190bool RenderLayerModelObject::shouldPlaceVerticalScrollbarOnLeft() const
191{
192// RTL Scrollbars require some system support, and this system support does not exist on certain versions of OS X. iOS uses a separate mechanism.
193#if PLATFORM(IOS_FAMILY)
194 return false;
195#else
196 switch (settings().userInterfaceDirectionPolicy()) {
197 case UserInterfaceDirectionPolicy::Content:
198 return style().shouldPlaceVerticalScrollbarOnLeft();
199 case UserInterfaceDirectionPolicy::System:
200 return settings().systemLayoutDirection() == TextDirection::RTL;
201 }
202 ASSERT_NOT_REACHED();
203 return style().shouldPlaceVerticalScrollbarOnLeft();
204#endif
205}
206
207std::optional<LayerRepaintRects> RenderLayerModelObject::layerRepaintRects() const
208{
209 return hasLayer() ? layer()->repaintRects() : std::nullopt;
210}
211
212bool RenderLayerModelObject::startAnimation(double timeOffset, const Animation& animation, const KeyframeList& keyframes)
213{
214 if (!layer() || !layer()->backing())
215 return false;
216 return layer()->backing()->startAnimation(timeOffset, animation, keyframes);
217}
218
219void RenderLayerModelObject::animationPaused(double timeOffset, const String& name)
220{
221 if (!layer() || !layer()->backing())
222 return;
223 layer()->backing()->animationPaused(timeOffset, name);
224}
225
226void RenderLayerModelObject::animationFinished(const String& name)
227{
228 if (!layer() || !layer()->backing())
229 return;
230 layer()->backing()->animationFinished(name);
231}
232
233void RenderLayerModelObject::transformRelatedPropertyDidChange()
234{
235 if (!layer() || !layer()->backing())
236 return;
237 layer()->backing()->transformRelatedPropertyDidChange();
238}
239
240void RenderLayerModelObject::suspendAnimations(MonotonicTime time)
241{
242 if (!layer() || !layer()->backing())
243 return;
244 layer()->backing()->suspendAnimations(time);
245}
246
247void RenderLayerModelObject::updateLayerTransform()
248{
249 // Transform-origin depends on box size, so we need to update the layer transform after layout.
250 if (hasLayer())
251 layer()->updateTransform();
252}
253
254#if ENABLE(LAYER_BASED_SVG_ENGINE)
255std::optional<LayoutRect> RenderLayerModelObject::computeVisibleRectInSVGContainer(const LayoutRect& rect, const RenderLayerModelObject* container, RenderObject::VisibleRectContext context) const
256{
257 ASSERT(is<RenderSVGModelObject>(this) || is<RenderSVGBlock>(this));
258 ASSERT(!style().hasInFlowPosition());
259 ASSERT(!view().frameView().layoutContext().isPaintOffsetCacheEnabled());
260
261 if (container == this)
262 return rect;
263
264 bool containerIsSkipped;
265 auto* localContainer = this->container(container, containerIsSkipped);
266 if (!localContainer)
267 return rect;
268
269 ASSERT_UNUSED(containerIsSkipped, !containerIsSkipped);
270
271 LayoutRect adjustedRect = rect;
272
273 // FIXME: [LBSE] Upstream RenderSVGForeignObject changes
274 // Move to origin of local coordinate system, if this is the first call to computeVisibleRectInContainer() originating
275 // from a SVG renderer (RenderSVGModelObject / RenderSVGBlock) or if we cross the boundary from HTML -> SVG via RenderSVGForeignObject.
276 // bool moveToOrigin = is<RenderSVGForeignObject>(renderer);
277 bool moveToOrigin = false;
278
279 /* FIXME: [LBSE] Upstream RenderObject changes
280 if (context.options.contains(RenderObject::VisibleRectContextOption::TranslateToSVGRendererOrigin)) {
281 context.options.remove(RenderObject::VisibleRectContextOption::TranslateToSVGRendererOrigin);
282 moveToOrigin = true;
283 }
284 */
285
286 if (moveToOrigin)
287 adjustedRect.moveBy(nominalSVGLayoutLocation());
288
289 if (auto* transform = layer()->transform())
290 adjustedRect = transform->mapRect(adjustedRect);
291
292 return localContainer->computeVisibleRectInContainer(adjustedRect, container, context);
293}
294
295void RenderLayerModelObject::mapLocalToSVGContainer(const RenderLayerModelObject* ancestorContainer, TransformState& transformState, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const
296{
297 // FIXME: [LBSE] Upstream RenderSVGBlock changes
298 // ASSERT(is<RenderSVGModelObject>(this) || is<RenderSVGBlock>(this));
299 ASSERT(is<RenderSVGModelObject>(this));
300 ASSERT(style().position() == PositionType::Static);
301
302 if (ancestorContainer == this)
303 return;
304
305 ASSERT(!view().frameView().layoutContext().isPaintOffsetCacheEnabled());
306
307 bool ancestorSkipped;
308 auto* container = this->container(ancestorContainer, ancestorSkipped);
309 if (!container)
310 return;
311
312 ASSERT_UNUSED(ancestorSkipped, !ancestorSkipped);
313
314 // If this box has a transform, it acts as a fixed position container for fixed descendants,
315 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
316 if (hasTransform())
317 mode.remove(IsFixed);
318
319 if (wasFixed)
320 *wasFixed = mode.contains(IsFixed);
321
322 auto containerOffset = offsetFromContainer(*container, LayoutPoint(transformState.mappedPoint()));
323
324 bool preserve3D = mode & UseTransforms && (container->style().preserves3D() || style().preserves3D());
325 if (mode & UseTransforms && shouldUseTransformFromContainer(container)) {
326 TransformationMatrix t;
327 getTransformFromContainer(container, containerOffset, t);
328 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
329 } else
330 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
331
332 mode.remove(ApplyContainerFlip);
333
334 container->mapLocalToContainer(ancestorContainer, transformState, mode, wasFixed);
335}
336
337void RenderLayerModelObject::applySVGTransform(TransformationMatrix& transform, SVGGraphicsElement& graphicsElement, const RenderStyle& style, const FloatRect& boundingBox, OptionSet<RenderStyle::TransformOperationOption> options) const
338{
339 // This check does not use style.hasTransformRelatedProperty() on purpose -- we only want to know if either the 'transform' property, an
340 // offset path, or the individual transform operations are set (perspective / transform-style: preserve-3d are not relevant here).
341 bool hasCSSTransform = style.hasTransform() || style.rotate() || style.translate() || style.scale();
342
343 bool affectedByTransformOrigin = false;
344 std::optional<AffineTransform> svgTransform;
345
346 if (hasCSSTransform)
347 affectedByTransformOrigin = style.affectedByTransformOrigin();
348 else if (auto affineTransform = graphicsElement.animatedLocalTransform(); !affineTransform.isIdentity()) {
349 svgTransform = affineTransform;
350 affectedByTransformOrigin = affineTransform.a() != 1 || affineTransform.b() || affineTransform.c() || affineTransform.d() != 1;
351 }
352
353 if (!hasCSSTransform && !svgTransform.has_value())
354 return;
355
356 FloatPoint3D originTranslate;
357 if (options.contains(RenderStyle::TransformOperationOption::TransformOrigin) && affectedByTransformOrigin)
358 originTranslate = style.computeTransformOrigin(boundingBox);
359
360 style.applyTransformOrigin(transform, originTranslate);
361
362 // CSS transforms take precedence over SVG transforms.
363 if (hasCSSTransform)
364 style.applyCSSTransform(transform, boundingBox, options);
365 else
366 transform.multiplyAffineTransform(svgTransform.value());
367
368 style.unapplyTransformOrigin(transform, originTranslate);
369}
370
371void RenderLayerModelObject::updateHasSVGTransformFlags(const SVGGraphicsElement& graphicsElement)
372{
373 bool hasSVGTransform = !graphicsElement.animatedLocalTransform().isIdentity();
374 setHasTransformRelatedProperty(style().hasTransformRelatedProperty() || hasSVGTransform);
375 setHasSVGTransform(hasSVGTransform);
376}
377#endif
378
379bool rendererNeedsPixelSnapping(const RenderLayerModelObject& renderer)
380{
381#if ENABLE(LAYER_BASED_SVG_ENGINE)
382 if (renderer.document().settings().layerBasedSVGEngineEnabled() && renderer.isSVGLayerAwareRenderer() && !renderer.isSVGRoot())
383 return false;
384#else
385 UNUSED_PARAM(renderer);
386#endif
387
388 return true;
389}
390
391FloatRect snapRectToDevicePixelsIfNeeded(const LayoutRect& rect, const RenderLayerModelObject& renderer)
392{
393 if (!rendererNeedsPixelSnapping(renderer))
394 return rect;
395 return snapRectToDevicePixels(rect, renderer.document().deviceScaleFactor());
396}
397
398FloatRect snapRectToDevicePixelsIfNeeded(const FloatRect& rect, const RenderLayerModelObject& renderer)
399{
400 if (!rendererNeedsPixelSnapping(renderer))
401 return rect;
402 return snapRectToDevicePixels(LayoutRect { rect }, renderer.document().deviceScaleFactor());
403}
404
405} // namespace WebCore
406
Note: See TracBrowser for help on using the repository browser.