| File: | Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/rendering/RenderLayerModelObject.cpp |
| Warning: | line 206, column 29 Local variable 'frameView' is uncounted and unsafe |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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-2025 Apple Inc. All rights reserved. |
| 7 | * Copyright (C) 2010-2015 Google Inc. All rights reserved. |
| 8 | * Copyright (C) 2023, 2024 Igalia S.L. |
| 9 | * |
| 10 | * This library is free software; you can redistribute it and/or |
| 11 | * modify it under the terms of the GNU Library General Public |
| 12 | * License as published by the Free Software Foundation; either |
| 13 | * version 2 of the License, or (at your option) any later version. |
| 14 | * |
| 15 | * This library is distributed in the hope that it will be useful, |
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 18 | * Library General Public License for more details. |
| 19 | * |
| 20 | * You should have received a copy of the GNU Library General Public License |
| 21 | * along with this library; see the file COPYING.LIB. If not, write to |
| 22 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 23 | * Boston, MA 02110-1301, USA. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "RenderLayerModelObject.h" |
| 28 | |
| 29 | #include "BlendingKeyframes.h" |
| 30 | #include "ContainerNodeInlines.h" |
| 31 | #include "InspectorInstrumentation.h" |
| 32 | #include "LocalFrameView.h" |
| 33 | #include "MotionPath.h" |
| 34 | #include "ReferencedSVGResources.h" |
| 35 | #include "RenderDescendantIterator.h" |
| 36 | #include "RenderElementInlines.h" |
| 37 | #include "RenderLayer.h" |
| 38 | #include "RenderLayerBacking.h" |
| 39 | #include "RenderLayerCompositor.h" |
| 40 | #include "RenderLayerScrollableArea.h" |
| 41 | #include "RenderMultiColumnSet.h" |
| 42 | #include "RenderObjectInlines.h" |
| 43 | #include "RenderSVGBlock.h" |
| 44 | #include "RenderSVGModelObject.h" |
| 45 | #include "RenderSVGResourceClipper.h" |
| 46 | #include "RenderSVGResourceFilter.h" |
| 47 | #include "RenderSVGResourceLinearGradient.h" |
| 48 | #include "RenderSVGResourceMarker.h" |
| 49 | #include "RenderSVGResourceMasker.h" |
| 50 | #include "RenderSVGResourceRadialGradient.h" |
| 51 | #include "RenderSVGText.h" |
| 52 | #include "RenderStyle+GettersInlines.h" |
| 53 | #include "RenderView.h" |
| 54 | #include "SVGClipPathElement.h" |
| 55 | #include "SVGFilterElement.h" |
| 56 | #include "SVGGraphicsElement.h" |
| 57 | #include "SVGMarkerElement.h" |
| 58 | #include "SVGMaskElement.h" |
| 59 | #include "SVGTextElement.h" |
| 60 | #include "SVGURIReference.h" |
| 61 | #include "Settings.h" |
| 62 | #include "StyleTransformResolver.h" |
| 63 | #include "TransformOperationData.h" |
| 64 | #include "TransformState.h" |
| 65 | #include <wtf/MathExtras.h> |
| 66 | #include <wtf/TZoneMallocInlines.h> |
| 67 | |
| 68 | namespace WebCore { |
| 69 | |
| 70 | WTF_MAKE_TZONE_ALLOCATED_IMPL(RenderLayerModelObject)::bmalloc::api::HeapRef RenderLayerModelObject::s_heapRef; const TZoneSpecification RenderLayerModelObject::s_heapSpec = { & RenderLayerModelObject::s_heapRef, TZoneSpecification::encodeSize <RenderLayerModelObject>(), TZoneSpecification::encodeAlignment <RenderLayerModelObject>(), TZoneSpecification::encodeCategory <RenderLayerModelObject>(), ::bmalloc::api::compactAllocationMode <RenderLayerModelObject>(), TZoneSpecification::encodeDescriptor <RenderLayerModelObject>(), }; void* RenderLayerModelObject ::operatorNewSlow(size_t size) { if constexpr (::bmalloc::api ::compactAllocationMode<RenderLayerModelObject>() == CompactAllocationMode ::Compact) return ::bmalloc::api::tzoneAllocateCompactSlow(size , s_heapSpec); return ::bmalloc::api::tzoneAllocateNonCompactSlow (size, s_heapSpec); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__((unused)) = int; |
| 71 | |
| 72 | bool RenderLayerModelObject::s_wasFloating = false; |
| 73 | bool RenderLayerModelObject::s_hadLayer = false; |
| 74 | bool RenderLayerModelObject::s_wasTransformed = false; |
| 75 | bool RenderLayerModelObject::s_layerWasSelfPainting = false; |
| 76 | |
| 77 | RenderLayerModelObject::RenderLayerModelObject(Type type, Element& element, RenderStyle&& style, OptionSet<TypeFlag> baseTypeFlags, TypeSpecificFlags typeSpecificFlags) |
| 78 | : RenderElement(type, element, WTF::move(style), baseTypeFlags | TypeFlag::IsLayerModelObject, typeSpecificFlags) |
| 79 | { |
| 80 | ASSERT(isRenderLayerModelObject())((void)0); |
| 81 | } |
| 82 | |
| 83 | RenderLayerModelObject::RenderLayerModelObject(Type type, Document& document, RenderStyle&& style, OptionSet<TypeFlag> baseTypeFlags, TypeSpecificFlags typeSpecificFlags) |
| 84 | : RenderElement(type, document, WTF::move(style), baseTypeFlags | TypeFlag::IsLayerModelObject, typeSpecificFlags) |
| 85 | { |
| 86 | ASSERT(isRenderLayerModelObject())((void)0); |
| 87 | } |
| 88 | |
| 89 | // Do not add any code in below destructor. Add it to willBeDestroyed() instead. |
| 90 | RenderLayerModelObject::~RenderLayerModelObject() = default; |
| 91 | |
| 92 | void RenderLayerModelObject::willBeDestroyed() |
| 93 | { |
| 94 | if (isPositioned()) { |
| 95 | if (style().hasViewportConstrainedPosition()) |
| 96 | view().frameView().removeViewportConstrainedObject(*this); |
| 97 | } |
| 98 | |
| 99 | destroyLayer(); |
| 100 | |
| 101 | RenderElement::willBeDestroyed(); |
| 102 | } |
| 103 | |
| 104 | void RenderLayerModelObject::destroyLayer() |
| 105 | { |
| 106 | setHasLayer(false); |
| 107 | m_layer = nullptr; |
| 108 | } |
| 109 | |
| 110 | void RenderLayerModelObject::createLayer() |
| 111 | { |
| 112 | ASSERT(!m_layer)((void)0); |
| 113 | m_layer = RenderLayer::create(*this); |
| 114 | setHasLayer(true); |
| 115 | m_layer->insertOnlyThisLayer(); |
| 116 | } |
| 117 | |
| 118 | bool RenderLayerModelObject::hasSelfPaintingLayer() const |
| 119 | { |
| 120 | return m_layer && m_layer->isSelfPaintingLayer(); |
| 121 | } |
| 122 | |
| 123 | void RenderLayerModelObject::styleWillChange(Style::Difference diff, const RenderStyle& newStyle) |
| 124 | { |
| 125 | s_wasFloating = isFloating(); |
| 126 | s_hadLayer = hasLayer(); |
| 127 | s_wasTransformed = isTransformed(); |
| 128 | if (s_hadLayer) |
| 129 | s_layerWasSelfPainting = layer()->isSelfPaintingLayer(); |
| 130 | |
| 131 | auto* oldStyle = hasInitializedStyle() ? &style() : nullptr; |
| 132 | if (diff == Style::DifferenceResult::RepaintLayer && parent() && oldStyle && oldStyle->clip() != newStyle.clip()) |
| 133 | layer()->clearClipRectsIncludingDescendants(); |
| 134 | RenderElement::styleWillChange(diff, newStyle); |
| 135 | } |
| 136 | |
| 137 | void RenderLayerModelObject::styleDidChange(Style::Difference diff, const RenderStyle* oldStyle) |
| 138 | { |
| 139 | updateFromStyle(); |
| 140 | RenderElement::styleDidChange(diff, oldStyle); |
| 141 | |
| 142 | // When an out-of-flow-positioned element changes its display between block and inline-block, |
| 143 | // then an incremental layout on the element's containing block lays out the element through |
| 144 | // LayoutPositionedObjects, which skips laying out the element's parent. |
| 145 | // The element's parent needs to relayout so that it calls |
| 146 | // RenderBlockFlow::setStaticInlinePositionForChild with the out-of-flow-positioned child, so |
| 147 | // that when it's laid out, its RenderBox::computeOutOfFlowPositionedLogicalWidth/Height takes into |
| 148 | // account its new inline/block position rather than its old block/inline position. |
| 149 | // Position changes and other types of display changes are handled elsewhere. |
| 150 | if ((oldStyle && isOutOfFlowPositioned() && parent() && (parent() != containingBlock())) |
| 151 | && (style().position() == oldStyle->position()) |
| 152 | && (style().originalDisplay().isInlineType() != oldStyle->originalDisplay().isInlineType()) |
| 153 | && (style().originalDisplay().isBlockType() || style().originalDisplay().isInlineType()) |
| 154 | && (oldStyle->originalDisplay().isBlockType() || oldStyle->originalDisplay().isInlineType())) |
| 155 | parent()->setChildNeedsLayout(); |
| 156 | |
| 157 | bool gainedOrLostLayer = false; |
| 158 | if (requiresLayer()) { |
| 159 | if (!layer() && layerCreationAllowedForSubtree()) { |
| 160 | gainedOrLostLayer = true; |
| 161 | if (s_wasFloating && isFloating()) |
| 162 | setChildNeedsLayout(); |
| 163 | createLayer(); |
| 164 | if (parent() && !needsLayout()) |
| 165 | layer()->setRepaintStatus(RepaintStatus::NeedsFullRepaint); |
| 166 | } |
| 167 | } else if (layer() && layer()->parent()) { |
| 168 | gainedOrLostLayer = true; |
| 169 | if (oldStyle && oldStyle->blendMode() != BlendMode::Normal) |
| 170 | layer()->willRemoveChildWithBlendMode(); |
| 171 | setHasTransformRelatedProperty(false); // All transform-related properties force layers, so we know we don't have one or the object doesn't support them. |
| 172 | setHasSVGTransform(false); // Same reason as for setHasTransformRelatedProperty(). |
| 173 | setHasReflection(false); |
| 174 | |
| 175 | // Repaint the about to be destroyed self-painting layer when style change also triggers repaint. |
| 176 | if (layer()->isSelfPaintingLayer() && layer()->repaintStatus() == RepaintStatus::NeedsFullRepaint && layer()->cachedClippedOverflowRect()) |
| 177 | repaintUsingContainer(containerForRepaint().renderer.get(), *(layer()->cachedClippedOverflowRect())); |
| 178 | |
| 179 | layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer |
| 180 | if (s_wasFloating && isFloating()) |
| 181 | setChildNeedsLayout(); |
| 182 | if (s_wasTransformed) |
| 183 | setNeedsLayoutAndPreferredWidthsUpdate(); |
| 184 | } |
| 185 | |
| 186 | if (gainedOrLostLayer) |
| 187 | InspectorInstrumentation::didAddOrRemoveScrollbars(*this); |
| 188 | |
| 189 | if (layer()) { |
| 190 | layer()->styleChanged(diff, oldStyle); |
| 191 | if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting) |
| 192 | setChildNeedsLayout(); |
| 193 | } |
| 194 | |
| 195 | bool newStyleIsViewportConstrained = style().hasViewportConstrainedPosition(); |
| 196 | bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportConstrainedPosition(); |
| 197 | if (newStyleIsViewportConstrained != oldStyleIsViewportConstrained) { |
| 198 | if (newStyleIsViewportConstrained && layer()) |
| 199 | view().frameView().addViewportConstrainedObject(*this); |
| 200 | else |
| 201 | view().frameView().removeViewportConstrainedObject(*this); |
| 202 | } |
| 203 | |
| 204 | if (oldStyle && !oldStyle->scrollPaddingEqual(style())) { |
| 205 | if (isDocumentElementRenderer()) { |
| 206 | LocalFrameView& frameView = view().frameView(); |
Local variable 'frameView' is uncounted and unsafe | |
| 207 | frameView.updateScrollbarSteps(); |
| 208 | } else if (CheckedPtr renderLayer = layer()) |
| 209 | renderLayer->updateScrollbarSteps(); |
| 210 | } |
| 211 | |
| 212 | if (oldStyle && !oldStyle->scrollSnapDataEquivalent(style())) { |
| 213 | if (auto* scrollSnapBox = enclosingScrollableContainer()) |
| 214 | scrollSnapBox->setNeedsLayout(); |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | bool RenderLayerModelObject::applyCachedClipAndScrollPosition(RepaintRects&, const RenderLayerModelObject*, VisibleRectContext) const |
| 219 | { |
| 220 | return false; |
| 221 | } |
| 222 | |
| 223 | bool RenderLayerModelObject::shouldPlaceVerticalScrollbarOnLeft() const |
| 224 | { |
| 225 | // RTL Scrollbars require some system support, and this system support does not exist on certain versions of macOS. iOS uses a separate mechanism. |
| 226 | #if PLATFORM(IOS_FAMILY)(defined 1 && 1) |
| 227 | return false; |
| 228 | #else |
| 229 | switch (settings().userInterfaceDirectionPolicy()) { |
| 230 | case UserInterfaceDirectionPolicy::Content: |
| 231 | return style().shouldPlaceVerticalScrollbarOnLeft(); |
| 232 | case UserInterfaceDirectionPolicy::System: |
| 233 | return settings().systemLayoutDirection() == TextDirection::RTL; |
| 234 | } |
| 235 | ASSERT_NOT_REACHED()((void)0); |
| 236 | return style().shouldPlaceVerticalScrollbarOnLeft(); |
| 237 | #endif |
| 238 | } |
| 239 | |
| 240 | std::optional<LayoutRect> RenderLayerModelObject::cachedLayerClippedOverflowRect() const |
| 241 | { |
| 242 | return hasLayer() ? layer()->cachedClippedOverflowRect() : std::nullopt; |
| 243 | } |
| 244 | |
| 245 | bool RenderLayerModelObject::startAnimation(double timeOffset, const GraphicsLayerAnimation& animation, const BlendingKeyframes& keyframes) |
| 246 | { |
| 247 | if (!layer() || !layer()->backing()) |
| 248 | return false; |
| 249 | return layer()->backing()->startAnimation(timeOffset, animation, keyframes); |
| 250 | } |
| 251 | |
| 252 | void RenderLayerModelObject::animationPaused(double timeOffset, const BlendingKeyframes& keyframes) |
| 253 | { |
| 254 | if (!layer() || !layer()->backing()) |
| 255 | return; |
| 256 | layer()->backing()->animationPaused(timeOffset, keyframes.acceleratedAnimationName()); |
| 257 | } |
| 258 | |
| 259 | void RenderLayerModelObject::animationFinished(const BlendingKeyframes& keyframes) |
| 260 | { |
| 261 | if (!layer() || !layer()->backing()) |
| 262 | return; |
| 263 | layer()->backing()->animationFinished(keyframes.acceleratedAnimationName()); |
| 264 | } |
| 265 | |
| 266 | void RenderLayerModelObject::transformRelatedPropertyDidChange() |
| 267 | { |
| 268 | if (!layer() || !layer()->backing()) |
| 269 | return; |
| 270 | layer()->backing()->transformRelatedPropertyDidChange(); |
| 271 | } |
| 272 | |
| 273 | void RenderLayerModelObject::suspendAnimations(MonotonicTime time) |
| 274 | { |
| 275 | if (!layer() || !layer()->backing()) |
| 276 | return; |
| 277 | layer()->backing()->suspendAnimations(time); |
| 278 | } |
| 279 | |
| 280 | TransformationMatrix* RenderLayerModelObject::layerTransform() const |
| 281 | { |
| 282 | if (hasLayer()) |
| 283 | return layer()->transform(); |
| 284 | return nullptr; |
| 285 | } |
| 286 | |
| 287 | void RenderLayerModelObject::updateLayerTransform() |
| 288 | { |
| 289 | if (auto* box = dynamicDowncast<RenderBox>(this); box && MotionPath::needsUpdateAfterContainingBlockLayout(style().offsetPath())) { |
| 290 | if (auto* containingBlock = this->containingBlock()) { |
| 291 | view().frameView().layoutContext().setBoxNeedsTransformUpdateAfterContainerLayout(*box, *containingBlock); |
| 292 | return; |
| 293 | } |
| 294 | } |
| 295 | // Transform-origin depends on box size, so we need to update the layer transform after layout. |
| 296 | if (hasLayer()) |
| 297 | layer()->updateTransform(); |
| 298 | } |
| 299 | |
| 300 | bool RenderLayerModelObject::shouldPaintSVGRenderer(const PaintInfo& paintInfo, const OptionSet<PaintPhase> relevantPaintPhases) const |
| 301 | { |
| 302 | if (paintInfo.context().paintingDisabled()) |
| 303 | return false; |
| 304 | |
| 305 | if (!relevantPaintPhases.isEmpty() && !relevantPaintPhases.contains(paintInfo.phase)) |
| 306 | return false; |
| 307 | |
| 308 | if (!paintInfo.shouldPaintWithinRoot(*this)) |
| 309 | return false; |
| 310 | |
| 311 | if (style().usedVisibility() == Visibility::Hidden || style().display() == Style::DisplayType::None) |
| 312 | return false; |
| 313 | |
| 314 | return true; |
| 315 | } |
| 316 | |
| 317 | auto RenderLayerModelObject::computeVisibleRectsInSVGContainer(const RepaintRects& rects, const RenderLayerModelObject* container, VisibleRectContext context) const -> std::optional<RepaintRects> |
| 318 | { |
| 319 | ASSERT(is<RenderSVGModelObject>(this) || is<RenderSVGBlock>(this))((void)0); |
| 320 | ASSERT(!style().hasInFlowPosition())((void)0); |
| 321 | ASSERT(!view().frameView().layoutContext().isPaintOffsetCacheEnabled())((void)0); |
| 322 | |
| 323 | if (container == this) |
| 324 | return rects; |
| 325 | |
| 326 | bool containerIsSkipped; |
| 327 | auto* localContainer = this->container(container, containerIsSkipped); |
| 328 | if (!localContainer) |
| 329 | return rects; |
| 330 | |
| 331 | ASSERT_UNUSED(containerIsSkipped, !containerIsSkipped)((void)containerIsSkipped); |
| 332 | |
| 333 | auto adjustedRects = rects; |
| 334 | |
| 335 | LayoutSize locationOffset; |
| 336 | if (CheckedPtr modelObject = dynamicDowncast<RenderSVGModelObject>(this)) |
| 337 | locationOffset = modelObject->locationOffsetEquivalent(); |
| 338 | else if (CheckedPtr svgBlock = dynamicDowncast<RenderSVGBlock>(this)) |
| 339 | locationOffset = svgBlock->locationOffset(); |
| 340 | |
| 341 | |
| 342 | // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box |
| 343 | // in the parent's coordinate space that encloses us. |
| 344 | if (hasLayer() && layer()->transform()) |
| 345 | adjustedRects.transform(*layer()->transform()); |
| 346 | |
| 347 | adjustedRects.move(locationOffset); |
| 348 | |
| 349 | if (localContainer->hasNonVisibleOverflow()) { |
| 350 | bool isEmpty = !downcast<RenderLayerModelObject>(*localContainer).applyCachedClipAndScrollPosition(adjustedRects, container, context); |
| 351 | if (isEmpty) { |
| 352 | if (context.options.contains(VisibleRectContext::Option::UseEdgeInclusiveIntersection)) |
| 353 | return std::nullopt; |
| 354 | return adjustedRects; |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | return localContainer->computeVisibleRectsInContainer(adjustedRects, container, context); |
| 359 | } |
| 360 | |
| 361 | void RenderLayerModelObject::mapLocalToSVGContainer(const RenderLayerModelObject* ancestorContainer, TransformState& transformState, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const |
| 362 | { |
| 363 | ASSERT(is<RenderSVGModelObject>(this) || is<RenderSVGBlock>(this))((void)0); |
| 364 | ASSERT(style().position() == PositionType::Static)((void)0); |
| 365 | |
| 366 | if (ancestorContainer == this) |
| 367 | return; |
| 368 | |
| 369 | ASSERT(!view().frameView().layoutContext().isPaintOffsetCacheEnabled())((void)0); |
| 370 | |
| 371 | bool ancestorSkipped; |
| 372 | auto* container = this->container(ancestorContainer, ancestorSkipped); |
| 373 | if (!container) |
| 374 | return; |
| 375 | |
| 376 | ASSERT_UNUSED(ancestorSkipped, !ancestorSkipped)((void)ancestorSkipped); |
| 377 | |
| 378 | // If this box has a transform, it acts as a fixed position container for fixed descendants, |
| 379 | // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. |
| 380 | if (isTransformed()) |
| 381 | mode.remove(IsFixed); |
| 382 | |
| 383 | if (wasFixed) |
| 384 | *wasFixed = mode.contains(IsFixed); |
| 385 | |
| 386 | auto containerOffset = offsetFromContainer(*container, LayoutPoint(transformState.mappedPoint())); |
| 387 | |
| 388 | pushOntoTransformState(transformState, mode, nullptr, container, containerOffset, false); |
| 389 | |
| 390 | mode.remove(ApplyContainerFlip); |
| 391 | |
| 392 | container->mapLocalToContainer(ancestorContainer, transformState, mode, wasFixed); |
| 393 | } |
| 394 | |
| 395 | void RenderLayerModelObject::applySVGTransform(TransformationMatrix& transform, const SVGGraphicsElement& graphicsElement, const RenderStyle& style, const FloatRect& boundingBox, const std::optional<AffineTransform>& preApplySVGTransformMatrix, const std::optional<AffineTransform>& postApplySVGTransformMatrix, OptionSet<Style::TransformResolverOption> options) const |
| 396 | { |
| 397 | auto svgTransform = graphicsElement.transform().concatenate(); |
| 398 | auto* supplementalTransform = graphicsElement.supplementalTransform(); // SMIL <animateMotion> |
| 399 | |
| 400 | // This check does not use style.hasTransformRelatedProperty() on purpose -- we only want to know if either the 'transform' property, an |
| 401 | // offset path, or the individual transform operations are set (perspective / transform-style: preserve-3d are not relevant here). |
| 402 | bool hasCSSTransform = !style.transform().isNone() |
| 403 | || !style.offsetPath().isNone() |
| 404 | || !style.rotate().isNone() |
| 405 | || !style.translate().isNone() |
| 406 | || !style.scale().isNone(); |
| 407 | bool hasSVGTransform = !svgTransform.isIdentity() || preApplySVGTransformMatrix || postApplySVGTransformMatrix || supplementalTransform; |
| 408 | |
| 409 | // Common case: 'viewBox' set on outermost <svg> element -> 'preApplySVGTransformMatrix' |
| 410 | // passed by RenderSVGViewportContainer::applyTransform(), the anonymous single child |
| 411 | // of RenderSVGRoot, that wraps all direct children from <svg> as present in DOM. All |
| 412 | // other transformations are unset (no need to compute transform-origin, etc. in that case). |
| 413 | if (!hasCSSTransform && !hasSVGTransform) |
| 414 | return; |
| 415 | |
| 416 | Style::TransformResolver transformResolver { transform, style }; |
| 417 | |
| 418 | auto affectedByTransformOrigin = [&]() { |
| 419 | if (preApplySVGTransformMatrix && !preApplySVGTransformMatrix->isIdentityOrTranslation()) |
| 420 | return true; |
| 421 | if (postApplySVGTransformMatrix && !postApplySVGTransformMatrix->isIdentityOrTranslation()) |
| 422 | return true; |
| 423 | if (supplementalTransform && !supplementalTransform->isIdentityOrTranslation()) |
| 424 | return true; |
| 425 | if (hasCSSTransform) |
| 426 | return transformResolver.affectedByTransformOrigin(); |
| 427 | return !svgTransform.isIdentityOrTranslation(); |
| 428 | }; |
| 429 | |
| 430 | FloatPoint3D originTranslate; |
| 431 | if (options.contains(Style::TransformResolverOption::TransformOrigin) && affectedByTransformOrigin()) |
| 432 | originTranslate = transformResolver.computeTransformOrigin(boundingBox); |
| 433 | |
| 434 | transformResolver.applyTransformOrigin(originTranslate); |
| 435 | |
| 436 | if (supplementalTransform) |
| 437 | transform.multiplyAffineTransform(*supplementalTransform); |
| 438 | |
| 439 | if (preApplySVGTransformMatrix) |
| 440 | transform.multiplyAffineTransform(preApplySVGTransformMatrix.value()); |
| 441 | |
| 442 | // CSS transforms take precedence over SVG transforms. |
| 443 | if (hasCSSTransform) |
| 444 | transformResolver.applyCSSTransform(TransformOperationData(boundingBox, this), options); |
| 445 | else if (!svgTransform.isIdentity()) |
| 446 | transform.multiplyAffineTransform(svgTransform); |
| 447 | |
| 448 | if (postApplySVGTransformMatrix) |
| 449 | transform.multiplyAffineTransform(postApplySVGTransformMatrix.value()); |
| 450 | |
| 451 | transformResolver.unapplyTransformOrigin(originTranslate); |
| 452 | } |
| 453 | |
| 454 | void RenderLayerModelObject::updateHasSVGTransformFlags() |
| 455 | { |
| 456 | ASSERT(document().settings().layerBasedSVGEngineEnabled())((void)0); |
| 457 | |
| 458 | bool hasSVGTransform = needsHasSVGTransformFlags(); |
| 459 | setHasTransformRelatedProperty(hasSVGTransform || style().hasTransformRelatedProperty()); |
| 460 | setHasSVGTransform(hasSVGTransform); |
| 461 | } |
| 462 | |
| 463 | RenderSVGResourceClipper* RenderLayerModelObject::svgClipperResourceFromStyle() const |
| 464 | { |
| 465 | if (!document().settings().layerBasedSVGEngineEnabled()) |
| 466 | return nullptr; |
| 467 | |
| 468 | return WTF::switchOn(style().clipPath(), |
| 469 | [&](const Style::ReferencePath& clipPath) -> RenderSVGResourceClipper* { |
| 470 | if (RefPtr referencedClipPathElement = ReferencedSVGResources::referencedClipPathElement(treeScopeForSVGReferences(), clipPath)) { |
| 471 | if (auto* referencedClipperRenderer = dynamicDowncast<RenderSVGResourceClipper>(referencedClipPathElement->renderer())) |
| 472 | return referencedClipperRenderer; |
| 473 | } |
| 474 | |
| 475 | if (RefPtr svgElement = dynamicDowncast<SVGElement>(this->element())) |
| 476 | document().addPendingSVGResource(clipPath.fragment(), *svgElement); |
| 477 | |
| 478 | return nullptr; |
| 479 | |
| 480 | }, |
| 481 | [&](const auto&) -> RenderSVGResourceClipper* { |
| 482 | return nullptr; |
| 483 | } |
| 484 | ); |
| 485 | } |
| 486 | |
| 487 | RenderSVGResourceFilter* RenderLayerModelObject::svgFilterResourceFromStyle() const |
| 488 | { |
| 489 | if (!document().settings().layerBasedSVGEngineEnabled()) |
| 490 | return nullptr; |
| 491 | |
| 492 | if (style().filter().size() != 1) |
| 493 | return nullptr; |
| 494 | |
| 495 | return WTF::switchOn(style().filter().first(), |
| 496 | [&](const Style::FilterReference& filterReference) -> RenderSVGResourceFilter* { |
| 497 | if (RefPtr referencedFilterElement = ReferencedSVGResources::referencedFilterElement(treeScopeForSVGReferences(), filterReference)) { |
| 498 | if (auto* referencedFilterRenderer = dynamicDowncast<RenderSVGResourceFilter>(referencedFilterElement->renderer())) |
| 499 | return referencedFilterRenderer; |
| 500 | } |
| 501 | |
| 502 | if (RefPtr svgElement = dynamicDowncast<SVGElement>(this->element())) |
| 503 | document().addPendingSVGResource(filterReference.cachedFragment, *svgElement); |
| 504 | |
| 505 | return nullptr; |
| 506 | }, |
| 507 | []<CSSValueID C, typename T>(const FunctionNotation<C, T>&) -> RenderSVGResourceFilter* { |
| 508 | return nullptr; |
| 509 | } |
| 510 | ); |
| 511 | } |
| 512 | |
| 513 | RenderSVGResourceMasker* RenderLayerModelObject::svgMaskerResourceFromStyle() const |
| 514 | { |
| 515 | if (!document().settings().layerBasedSVGEngineEnabled()) |
| 516 | return nullptr; |
| 517 | |
| 518 | RefPtr maskImage = style().maskLayers().usedFirst().image().tryStyleImage(); |
| 519 | if (!maskImage) |
| 520 | return nullptr; |
| 521 | |
| 522 | auto resourceID = SVGURIReference::fragmentIdentifierFromIRIString(maskImage->url(), protect(document())); |
| 523 | |
| 524 | if (RefPtr referencedMaskElement = ReferencedSVGResources::referencedMaskElement(treeScopeForSVGReferences(), *maskImage)) { |
| 525 | if (auto* referencedMaskerRenderer = dynamicDowncast<RenderSVGResourceMasker>(referencedMaskElement->renderer())) |
| 526 | return referencedMaskerRenderer; |
| 527 | } |
| 528 | |
| 529 | if (RefPtr element = this->element()) |
| 530 | document().addPendingSVGResource(resourceID, downcast<SVGElement>(*element)); |
| 531 | |
| 532 | return nullptr; |
| 533 | } |
| 534 | |
| 535 | RenderSVGResourceMarker* RenderLayerModelObject::svgMarkerStartResourceFromStyle() const |
| 536 | { |
| 537 | return svgMarkerResourceFromStyle(style().markerStart()); |
| 538 | } |
| 539 | |
| 540 | RenderSVGResourceMarker* RenderLayerModelObject::svgMarkerMidResourceFromStyle() const |
| 541 | { |
| 542 | return svgMarkerResourceFromStyle(style().markerMid()); |
| 543 | } |
| 544 | |
| 545 | RenderSVGResourceMarker* RenderLayerModelObject::svgMarkerEndResourceFromStyle() const |
| 546 | { |
| 547 | return svgMarkerResourceFromStyle(style().markerEnd()); |
| 548 | } |
| 549 | |
| 550 | RenderSVGResourceMarker* RenderLayerModelObject::svgMarkerResourceFromStyle(const Style::SVGMarkerResource& markerResource) const |
| 551 | { |
| 552 | if (!document().settings().layerBasedSVGEngineEnabled()) |
| 553 | return nullptr; |
| 554 | |
| 555 | auto markerResourceURL = markerResource.tryURL(); |
| 556 | if (!markerResourceURL) |
| 557 | return nullptr; |
| 558 | |
| 559 | if (RefPtr referencedMarkerElement = ReferencedSVGResources::referencedMarkerElement(treeScopeForSVGReferences(), *markerResourceURL)) { |
| 560 | if (auto* referencedMarkerRenderer = dynamicDowncast<RenderSVGResourceMarker>(referencedMarkerElement->renderer())) |
| 561 | return referencedMarkerRenderer; |
| 562 | } |
| 563 | |
| 564 | if (RefPtr element = dynamicDowncast<SVGElement>(this->element())) { |
| 565 | auto resourceID = SVGURIReference::fragmentIdentifierFromIRIString(markerResourceURL->resolved.string(), document()); |
| 566 | document().addPendingSVGResource(resourceID, *element); |
| 567 | } |
| 568 | |
| 569 | return nullptr; |
| 570 | } |
| 571 | |
| 572 | RenderSVGResourcePaintServer* RenderLayerModelObject::svgFillPaintServerResourceFromStyle(const RenderStyle& style) const |
| 573 | { |
| 574 | if (!document().settings().layerBasedSVGEngineEnabled()) |
| 575 | return nullptr; |
| 576 | |
| 577 | auto fillURL = style.fill().tryAnyURL(); |
| 578 | if (!fillURL) |
| 579 | return nullptr; |
| 580 | |
| 581 | if (RefPtr referencedElement = ReferencedSVGResources::referencedPaintServerElement(treeScopeForSVGReferences(), *fillURL)) { |
| 582 | if (auto* referencedPaintServerRenderer = dynamicDowncast<RenderSVGResourcePaintServer>(referencedElement->renderer())) |
| 583 | return referencedPaintServerRenderer; |
| 584 | } |
| 585 | |
| 586 | if (RefPtr element = this->element()) |
| 587 | document().addPendingSVGResource(AtomString(fillURL->resolved.string()), downcast<SVGElement>(*element)); |
| 588 | |
| 589 | return nullptr; |
| 590 | } |
| 591 | |
| 592 | RenderSVGResourcePaintServer* RenderLayerModelObject::svgStrokePaintServerResourceFromStyle(const RenderStyle& style) const |
| 593 | { |
| 594 | if (!document().settings().layerBasedSVGEngineEnabled()) |
| 595 | return nullptr; |
| 596 | |
| 597 | auto strokeURL = style.stroke().tryAnyURL(); |
| 598 | if (!strokeURL) |
| 599 | return nullptr; |
| 600 | |
| 601 | if (RefPtr referencedElement = ReferencedSVGResources::referencedPaintServerElement(treeScopeForSVGReferences(), *strokeURL)) { |
| 602 | if (auto* referencedPaintServerRenderer = dynamicDowncast<RenderSVGResourcePaintServer>(referencedElement->renderer())) |
| 603 | return referencedPaintServerRenderer; |
| 604 | } |
| 605 | |
| 606 | if (RefPtr element = this->element()) |
| 607 | document().addPendingSVGResource(AtomString(strokeURL->resolved.string()), downcast<SVGElement>(*element)); |
| 608 | |
| 609 | return nullptr; |
| 610 | } |
| 611 | |
| 612 | LegacyRenderSVGResourceClipper* RenderLayerModelObject::legacySVGClipperResourceFromStyle() const |
| 613 | { |
| 614 | return WTF::switchOn(style().clipPath(), |
| 615 | [&](const Style::ReferencePath& clipPath) -> LegacyRenderSVGResourceClipper* { |
| 616 | return ReferencedSVGResources::referencedClipperRenderer(treeScopeForSVGReferences(), clipPath); |
| 617 | }, |
| 618 | [&](const auto&) -> LegacyRenderSVGResourceClipper* { |
| 619 | return nullptr; |
| 620 | } |
| 621 | ); |
| 622 | } |
| 623 | |
| 624 | bool RenderLayerModelObject::pointInSVGClippingArea(const FloatPoint& point) const |
| 625 | { |
| 626 | auto clipPathReferenceBox = [&](CSSBoxType boxType) -> FloatRect { |
| 627 | FloatRect referenceBox; |
| 628 | switch (boxType) { |
| 629 | case CSSBoxType::BorderBox: |
| 630 | case CSSBoxType::MarginBox: |
| 631 | case CSSBoxType::StrokeBox: |
| 632 | case CSSBoxType::BoxMissing: |
| 633 | // FIXME: strokeBoundingBox() takes dasharray into account but shouldn't. |
| 634 | referenceBox = strokeBoundingBox(); |
| 635 | break; |
| 636 | case CSSBoxType::ViewBox: |
| 637 | if (element()) { |
| 638 | // FIXME: [LBSE] This should not need to use SVGLengthContext, RenderSVGRoot holds that information. |
| 639 | auto viewportSize = SVGLengthContext(downcast<SVGElement>(element())).viewportSize(); |
| 640 | if (viewportSize) |
| 641 | referenceBox.setSize(*viewportSize); |
| 642 | break; |
| 643 | } |
| 644 | [[fallthrough]]; |
| 645 | case CSSBoxType::ContentBox: |
| 646 | case CSSBoxType::FillBox: |
| 647 | case CSSBoxType::PaddingBox: |
| 648 | referenceBox = objectBoundingBox(); |
| 649 | break; |
| 650 | } |
| 651 | return referenceBox; |
| 652 | }; |
| 653 | |
| 654 | return WTF::switchOn(style().clipPath(), |
| 655 | [&](const Style::BasicShapePath& clipPath) { |
| 656 | auto referenceBox = clipPathReferenceBox(clipPath.referenceBox()); |
| 657 | if (!referenceBox.contains(point)) |
| 658 | return false; |
| 659 | return Style::path(clipPath.shape(), referenceBox, style().usedZoomForLength()).contains(point, Style::windRule(clipPath.shape())); |
| 660 | }, |
| 661 | [&](const Style::BoxPath& clipPath) { |
| 662 | auto referenceBox = clipPathReferenceBox(clipPath.referenceBox()); |
| 663 | if (!referenceBox.contains(point)) |
| 664 | return false; |
| 665 | return FloatRoundedRect { referenceBox }.path().contains(point); |
| 666 | }, |
| 667 | [&](const auto&) { |
| 668 | if (auto* referencedClipperRenderer = svgClipperResourceFromStyle()) |
| 669 | return referencedClipperRenderer->hitTestClipContent(objectBoundingBox(), LayoutPoint(point)); |
| 670 | return true; |
| 671 | } |
| 672 | ); |
| 673 | } |
| 674 | |
| 675 | void RenderLayerModelObject::repaintOrRelayoutAfterSVGTransformChange() |
| 676 | { |
| 677 | ASSERT(document().settings().layerBasedSVGEngineEnabled())((void)0); |
| 678 | |
| 679 | updateHasSVGTransformFlags(); |
| 680 | |
| 681 | // LBSE shares the text rendering code with the legacy SVG engine, largely unmodified. |
| 682 | // At present text layout depends on transformations ('screen font scaling factor' is used to |
| 683 | // determine which font to use for layout / painting). Therefore if the x/y scaling factors |
| 684 | // of the transformation matrix changes due to the transform update, we have to recompute the text metrics |
| 685 | // of all RenderSVGText descendants of the renderer in the ancestor chain, that will receive the transform |
| 686 | // update. |
| 687 | // |
| 688 | // There is no intrinsic reason for that, besides historical ones. If we decouple |
| 689 | // the 'font size screen scaling factor' from layout and only use it during painting |
| 690 | // we can optimize transformations for text, simply by avoid the need for layout. |
| 691 | auto previousTransform = layerTransform() ? layerTransform()->toAffineTransform() : identity; |
| 692 | updateLayerTransform(); |
| 693 | |
| 694 | auto currentTransform = layerTransform() ? layerTransform()->toAffineTransform() : identity; |
| 695 | |
| 696 | // We have to force a stacking context if we did not have a transform before. Normally |
| 697 | // RenderLayer::styleChanged does this for us but repaintOrRelayoutAfterSVGTransformChange |
| 698 | // does not end up calling it. |
| 699 | if (previousTransform.isIdentity() && !currentTransform.isIdentity()) { |
| 700 | if (hasLayer()) |
| 701 | layer()->forceStackingContextIfNeeded(); |
| 702 | } |
| 703 | |
| 704 | auto determineIfLayerTransformChangeModifiesScale = [&]() -> bool { |
| 705 | if (previousTransform == currentTransform) |
| 706 | return false; |
| 707 | |
| 708 | // Only if the effective x/y scale changes, a re-layout is necessary, due to changed on-screen scaling factors. |
| 709 | // The next RenderSVGText layout will see a different 'screen font scaling factor', different text metrics etc. |
| 710 | if (!WTF::areEssentiallyEqual(previousTransform.xScale(), currentTransform.xScale())) |
| 711 | return true; |
| 712 | |
| 713 | if (!WTF::areEssentiallyEqual(previousTransform.yScale(), currentTransform.yScale())) |
| 714 | return true; |
| 715 | |
| 716 | return false; |
| 717 | }(); |
| 718 | |
| 719 | if (determineIfLayerTransformChangeModifiesScale) { |
| 720 | if (auto* textAffectedByTransformChange = dynamicDowncast<RenderSVGText>(this)) { |
| 721 | // Mark text metrics for update, and only trigger a relayout and not an explicit repaint. |
| 722 | textAffectedByTransformChange->setNeedsTextMetricsUpdate(); |
| 723 | textAffectedByTransformChange->textElement().updateSVGRendererForElementChange(); |
| 724 | return; |
| 725 | } |
| 726 | |
| 727 | // Recursively mark text metrics for update in all descendant RenderSVGText objects. |
| 728 | bool markedAny = false; |
| 729 | for (auto& textDescendantAffectedByTransformChange : descendantsOfType<RenderSVGText>(*this)) { |
| 730 | textDescendantAffectedByTransformChange.setNeedsTextMetricsUpdate(); |
| 731 | textDescendantAffectedByTransformChange.textElement().updateSVGRendererForElementChange(); |
| 732 | if (!markedAny) |
| 733 | markedAny = true; |
| 734 | } |
| 735 | |
| 736 | // If we marked a text descendant for relayout, we are expecting a relayout ourselves, so no reason for an explicit repaint(). |
| 737 | if (markedAny) |
| 738 | return; |
| 739 | } |
| 740 | |
| 741 | // Instead of performing a full-fledged layout (issuing repaints), just recompute the layer transform, and repaint. |
| 742 | // In LBSE transformations do not affect the layout (except for text, where it still does!) -- SVG follows closely the CSS/HTML route, to avoid costly layouts. |
| 743 | repaintRendererOrClientsOfReferencedSVGResources(); |
| 744 | } |
| 745 | |
| 746 | void RenderLayerModelObject::paintSVGClippingMask(PaintInfo& paintInfo, const FloatRect& objectBoundingBox) const |
| 747 | { |
| 748 | ASSERT(paintInfo.phase == PaintPhase::ClippingMask)((void)0); |
| 749 | auto& context = paintInfo.context(); |
| 750 | if (!paintInfo.shouldPaintWithinRoot(*this) || style().usedVisibility() != Visibility::Visible || context.paintingDisabled()) |
| 751 | return; |
| 752 | |
| 753 | ASSERT(document().settings().layerBasedSVGEngineEnabled())((void)0); |
| 754 | if (auto* referencedClipperRenderer = svgClipperResourceFromStyle()) |
| 755 | referencedClipperRenderer->applyMaskClipping(paintInfo, *this, objectBoundingBox); |
| 756 | } |
| 757 | |
| 758 | void RenderLayerModelObject::paintSVGMask(PaintInfo& paintInfo, const LayoutPoint& adjustedPaintOffset) const |
| 759 | { |
| 760 | ASSERT(paintInfo.phase == PaintPhase::Mask)((void)0); |
| 761 | auto& context = paintInfo.context(); |
| 762 | if (!paintInfo.shouldPaintWithinRoot(*this) || context.paintingDisabled()) |
| 763 | return; |
| 764 | |
| 765 | ASSERT(isSVGLayerAwareRenderer())((void)0); |
| 766 | if (auto* referencedMaskerRenderer = svgMaskerResourceFromStyle()) |
| 767 | referencedMaskerRenderer->applyMask(paintInfo, *this, adjustedPaintOffset); |
| 768 | } |
| 769 | |
| 770 | void RenderLayerModelObject::paintSVGEventRegion(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| 771 | { |
| 772 | ASSERT(paintInfo.phase == PaintPhase::EventRegion)((void)0); |
| 773 | if (style().usedVisibility() == Visibility::Hidden || objectBoundingBox().isEmpty()) |
| 774 | return; |
| 775 | |
| 776 | auto adjustedPaintOffset = paintOffset + currentSVGLayoutLocation(); |
| 777 | auto coordinateSystemOriginTranslation = adjustedPaintOffset - nominalSVGLayoutLocation(); |
| 778 | auto eventRegionBounds = strokeBoundingBox(); |
| 779 | eventRegionBounds.move(coordinateSystemOriginTranslation); |
| 780 | paintInfo.eventRegionContext()->unite(FloatRoundedRect(eventRegionBounds), *this, style(), false); |
| 781 | } |
| 782 | |
| 783 | #if ASSERT_ENABLED0 |
| 784 | bool RenderLayerModelObject::layerAccessPreventedSlow() const |
| 785 | { |
| 786 | return view().frameView().layerAccessPrevented(); |
| 787 | } |
| 788 | #endif |
| 789 | |
| 790 | bool rendererNeedsPixelSnapping(const RenderLayerModelObject& renderer) |
| 791 | { |
| 792 | if (renderer.document().settings().layerBasedSVGEngineEnabled() && renderer.isSVGLayerAwareRenderer() && !renderer.isRenderSVGRoot()) |
| 793 | return false; |
| 794 | return true; |
| 795 | } |
| 796 | |
| 797 | FloatRect snapRectToDevicePixelsIfNeeded(const LayoutRect& rect, const RenderLayerModelObject& renderer) |
| 798 | { |
| 799 | if (!rendererNeedsPixelSnapping(renderer)) |
| 800 | return rect; |
| 801 | return snapRectToDevicePixels(rect, renderer.document().deviceScaleFactor()); |
| 802 | } |
| 803 | |
| 804 | FloatRect snapRectToDevicePixelsIfNeeded(const FloatRect& rect, const RenderLayerModelObject& renderer) |
| 805 | { |
| 806 | if (!rendererNeedsPixelSnapping(renderer)) |
| 807 | return rect; |
| 808 | return snapRectToDevicePixels(LayoutRect { rect }, renderer.document().deviceScaleFactor()); |
| 809 | } |
| 810 | |
| 811 | } // namespace WebCore |
| 812 |