| File: | Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/rendering/RenderInline.cpp |
| Warning: | line 884, column 82 Call argument 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 | * Copyright (C) 2003-2025 Apple Inc. All rights reserved. |
| 5 | * Copyright (C) 2014 Google Inc. All rights reserved. |
| 6 | * Copyright (C) 2025 Samuel Weinig <sam@webkit.org> |
| 7 | * |
| 8 | * This library is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU Library General Public |
| 10 | * License as published by the Free Software Foundation; either |
| 11 | * version 2 of the License, or (at your option) any later version. |
| 12 | * |
| 13 | * This library is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | * Library General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU Library General Public License |
| 19 | * along with this library; see the file COPYING.LIB. If not, write to |
| 20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 21 | * Boston, MA 02110-1301, USA. |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | #include "config.h" |
| 26 | #include "RenderInline.h" |
| 27 | |
| 28 | #include "Chrome.h" |
| 29 | #include "FloatQuad.h" |
| 30 | #include "FrameSelection.h" |
| 31 | #include "GraphicsContext.h" |
| 32 | #include "HitTestResult.h" |
| 33 | #include "InlineIteratorBoxInlines.h" |
| 34 | #include "InlineIteratorInlineBox.h" |
| 35 | #include "InlineIteratorLineBox.h" |
| 36 | #include "LayoutIntegrationLineLayout.h" |
| 37 | #include "LegacyInlineTextBox.h" |
| 38 | #include "OutlinePainter.h" |
| 39 | #include "RenderBlock.h" |
| 40 | #include "RenderBoxInlines.h" |
| 41 | #include "RenderChildIterator.h" |
| 42 | #include "RenderElementStyleInlines.h" |
| 43 | #include "RenderElementInlines.h" |
| 44 | #include "RenderFragmentedFlow.h" |
| 45 | #include "RenderGeometryMap.h" |
| 46 | #include "RenderIterator.h" |
| 47 | #include "RenderLayer.h" |
| 48 | #include "RenderLayoutState.h" |
| 49 | #include "RenderLineBreak.h" |
| 50 | #include "RenderListMarker.h" |
| 51 | #include "RenderObjectInlines.h" |
| 52 | #include "RenderTable.h" |
| 53 | #include "RenderTheme.h" |
| 54 | #include "RenderTreeBuilder.h" |
| 55 | #include "RenderView.h" |
| 56 | #include "Settings.h" |
| 57 | #include "TransformState.h" |
| 58 | #include "VisiblePosition.h" |
| 59 | #include <wtf/SetForScope.h> |
| 60 | #include <wtf/TZoneMallocInlines.h> |
| 61 | |
| 62 | namespace WebCore { |
| 63 | |
| 64 | WTF_MAKE_TZONE_ALLOCATED_IMPL(RenderInline)::bmalloc::api::HeapRef RenderInline::s_heapRef; const TZoneSpecification RenderInline::s_heapSpec = { &RenderInline::s_heapRef, TZoneSpecification ::encodeSize<RenderInline>(), TZoneSpecification::encodeAlignment <RenderInline>(), TZoneSpecification::encodeCategory< RenderInline>(), ::bmalloc::api::compactAllocationMode< RenderInline>(), TZoneSpecification::encodeDescriptor<RenderInline >(), }; void* RenderInline::operatorNewSlow(size_t size) { if constexpr (::bmalloc::api::compactAllocationMode<RenderInline >() == CompactAllocationMode::Compact) return ::bmalloc::api ::tzoneAllocateCompactSlow(size, s_heapSpec); return ::bmalloc ::api::tzoneAllocateNonCompactSlow(size, s_heapSpec); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__( (unused)) = int; |
| 65 | |
| 66 | RenderInline::RenderInline(Type type, Element& element, RenderStyle&& style) |
| 67 | : RenderBoxModelObject(type, element, WTF::move(style), TypeFlag::IsRenderInline, { }) |
| 68 | { |
| 69 | setChildrenInline(true); |
| 70 | ASSERT(isRenderInline())((void)0); |
| 71 | } |
| 72 | |
| 73 | RenderInline::RenderInline(Type type, Document& document, RenderStyle&& style) |
| 74 | : RenderBoxModelObject(type, document, WTF::move(style), TypeFlag::IsRenderInline, { }) |
| 75 | { |
| 76 | setChildrenInline(true); |
| 77 | ASSERT(isRenderInline())((void)0); |
| 78 | } |
| 79 | |
| 80 | RenderInline::~RenderInline() = default; |
| 81 | |
| 82 | void RenderInline::willBeDestroyed() |
| 83 | { |
| 84 | #if ASSERT_ENABLED0 |
| 85 | // Make sure we do not retain "this" in the continuation outline table map of our containing blocks. |
| 86 | if (parent() && style().usedVisibility() == Visibility::Visible && hasOutline()) { |
| 87 | bool containingBlockPaintsContinuationOutline = continuation() || isContinuation(); |
| 88 | if (containingBlockPaintsContinuationOutline) { |
| 89 | if (RenderBlock* cb = containingBlock()) { |
| 90 | if (RenderBlock* cbCb = cb->containingBlock()) |
| 91 | ASSERT(!cbCb->paintsContinuationOutline(*this))((void)0); |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | #endif // ASSERT_ENABLED |
| 96 | |
| 97 | if (!renderTreeBeingDestroyed()) { |
| 98 | if (auto* inlineBox = firstLegacyInlineBox()) { |
| 99 | // We can't wait for RenderBoxModelObject::destroy to clear the selection, |
| 100 | // because by then we will have nuked the line boxes. |
| 101 | if (isSelectionBorder()) |
| 102 | frame().selection().setNeedsSelectionUpdate(); |
| 103 | |
| 104 | // If line boxes are contained inside a root, that means we're an inline. |
| 105 | // In that case, we need to remove all the line boxes so that the parent |
| 106 | // lines aren't pointing to deleted children. If the first line box does |
| 107 | // not have a parent that means they are either already disconnected or |
| 108 | // root lines that can just be destroyed without disconnecting. |
| 109 | if (inlineBox->parent()) { |
| 110 | for (auto* box = inlineBox; box; box = box->nextLineBox()) |
| 111 | box->removeFromParent(); |
| 112 | } |
| 113 | } else if (auto* parent = this->parent(); parent && parent->isSVGRenderer()) |
| 114 | parent->dirtyLineFromChangedChild(); |
| 115 | } |
| 116 | |
| 117 | m_legacyLineBoxes.deleteLineBoxes(); |
| 118 | |
| 119 | RenderBoxModelObject::willBeDestroyed(); |
| 120 | } |
| 121 | |
| 122 | void RenderInline::updateFromStyle() |
| 123 | { |
| 124 | RenderBoxModelObject::updateFromStyle(); |
| 125 | |
| 126 | // FIXME: Support transforms and reflections on inline flows someday. |
| 127 | setHasTransformRelatedProperty(false); |
| 128 | setHasReflection(false); |
| 129 | } |
| 130 | |
| 131 | static RenderElement* NODELETE[[clang::annotate_type("webkit.nodelete")]] inFlowPositionedInlineAncestor(RenderElement* p) |
| 132 | { |
| 133 | while (p && p->isRenderInline()) { |
| 134 | if (p->isInFlowPositioned()) |
| 135 | return p; |
| 136 | p = p->parent(); |
| 137 | } |
| 138 | return nullptr; |
| 139 | } |
| 140 | |
| 141 | static void updateStyleOfAnonymousBlockContinuations(const RenderBlock& block, const RenderStyle* newStyle, const RenderStyle* oldStyle) |
| 142 | { |
| 143 | // If any descendant blocks exist then they will be in the next anonymous block and its siblings. |
| 144 | for (RenderBox* box = block.nextSiblingBox(); box && box->isAnonymousBlock(); box = box->nextSiblingBox()) { |
| 145 | if (box->style().position() == newStyle->position()) |
| 146 | continue; |
| 147 | |
| 148 | CheckedPtr block = dynamicDowncast<RenderBlock>(*box); |
| 149 | if (!block) |
| 150 | continue; |
| 151 | |
| 152 | if (!block->isContinuation()) |
| 153 | continue; |
| 154 | |
| 155 | // If we are no longer in-flow positioned but our descendant block(s) still have an in-flow positioned ancestor then |
| 156 | // their containing anonymous block should keep its in-flow positioning. |
| 157 | RenderInline* continuation = block->inlineContinuation(); |
| 158 | if (oldStyle->hasInFlowPosition() && inFlowPositionedInlineAncestor(continuation)) |
| 159 | continue; |
| 160 | auto blockStyle = RenderStyle::createAnonymousStyleWithDisplay(block->style(), Style::DisplayType::BlockFlow); |
| 161 | blockStyle.setPosition(newStyle->position()); |
| 162 | block->setStyle(WTF::move(blockStyle)); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | void RenderInline::styleWillChange(Style::Difference diff, const RenderStyle& newStyle) |
| 167 | { |
| 168 | RenderBoxModelObject::styleWillChange(diff, newStyle); |
| 169 | |
| 170 | // RenderInlines forward their absolute positioned descendants to their (non-anonymous) containing block. |
| 171 | // Check if this non-anonymous containing block can hold the absolute positioned elements when the inline is no longer positioned. |
| 172 | CheckedPtr container = containingBlock(); |
| 173 | if (!container) |
| 174 | return; |
| 175 | |
| 176 | const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr; |
| 177 | if (oldStyle) |
| 178 | removeOutOfFlowBoxesIfNeededOnStyleChange(*container, *oldStyle, newStyle); |
| 179 | } |
| 180 | |
| 181 | void RenderInline::styleDidChange(Style::Difference diff, const RenderStyle* oldStyle) |
| 182 | { |
| 183 | RenderBoxModelObject::styleDidChange(diff, oldStyle); |
| 184 | |
| 185 | // Ensure that all of the split inlines pick up the new style. We |
| 186 | // only do this if we're an inline, since we don't want to propagate |
| 187 | // a block's style to the other inlines. |
| 188 | // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before |
| 189 | // and after the block share the same style, but the block doesn't |
| 190 | // need to pass its style on to anyone else. |
| 191 | auto& newStyle = style(); |
| 192 | RenderInline* continuation = inlineContinuation(); |
| 193 | if (continuation && !isContinuation()) { |
| 194 | for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineContinuation()) |
| 195 | currCont->setStyle(RenderStyle::clone(newStyle)); |
| 196 | // If an inline's in-flow positioning has changed and it is part of an active continuation as a descendant of an anonymous containing block, |
| 197 | // then any descendant blocks will need to change their in-flow positioning accordingly. |
| 198 | // Do this by updating the position of the descendant blocks' containing anonymous blocks - there may be more than one. |
| 199 | if (containingBlock()->isAnonymousBlock() && oldStyle && newStyle.position() != oldStyle->position() && (newStyle.hasInFlowPosition() || oldStyle->hasInFlowPosition())) |
| 200 | updateStyleOfAnonymousBlockContinuations(*containingBlock(), &newStyle, oldStyle); |
| 201 | } |
| 202 | |
| 203 | propagateStyleToAnonymousChildren(StylePropagationType::AllChildren); |
| 204 | } |
| 205 | |
| 206 | bool RenderInline::mayAffectLayout() const |
| 207 | { |
| 208 | auto* parentStyle = &parent()->style(); |
| 209 | auto* parentRenderInline = dynamicDowncast<RenderInline>(*parent()); |
| 210 | auto hasHardLineBreakChildOnly = firstChild() && firstChild() == lastChild() && firstChild()->isBR(); |
| 211 | bool checkFonts = document().inNoQuirksMode(); |
| 212 | auto mayAffectLayout = (parentRenderInline && parentRenderInline->mayAffectLayout()) |
| 213 | || (parentRenderInline && !WTF::holdsAlternative<CSS::Keyword::Baseline>(parentStyle->verticalAlign())) |
| 214 | || !WTF::holdsAlternative<CSS::Keyword::Baseline>(style().verticalAlign()) |
| 215 | || !style().textEmphasisStyle().isNone() |
| 216 | || (checkFonts && (!parentStyle->fontCascade().metricsOfPrimaryFont().hasIdenticalAscentDescentAndLineGap(style().fontCascade().metricsOfPrimaryFont()) |
| 217 | || parentStyle->lineHeight() != style().lineHeight())) |
| 218 | || hasHardLineBreakChildOnly; |
| 219 | |
| 220 | if (!mayAffectLayout && checkFonts) { |
| 221 | // Have to check the first line style as well. |
| 222 | parentStyle = &parent()->firstLineStyle(); |
| 223 | auto& childStyle = firstLineStyle(); |
| 224 | mayAffectLayout = !parentStyle->fontCascade().metricsOfPrimaryFont().hasIdenticalAscentDescentAndLineGap(childStyle.fontCascade().metricsOfPrimaryFont()) |
| 225 | || !WTF::holdsAlternative<CSS::Keyword::Baseline>(childStyle.verticalAlign()) |
| 226 | || parentStyle->lineHeight() != childStyle.lineHeight(); |
| 227 | } |
| 228 | return mayAffectLayout; |
| 229 | } |
| 230 | |
| 231 | void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| 232 | { |
| 233 | if (auto* lineLayout = LayoutIntegration::LineLayout::containing(*this)) |
| 234 | lineLayout->paint(paintInfo, paintOffset, this); |
| 235 | } |
| 236 | |
| 237 | template<typename GeneratorContext> |
| 238 | void RenderInline::generateLineBoxRects(GeneratorContext& context) const |
| 239 | { |
| 240 | if (auto* lineLayout = LayoutIntegration::LineLayout::containing(*this)) { |
| 241 | auto inlineBoxRects = lineLayout->collectInlineBoxRects(*this); |
| 242 | if (inlineBoxRects.isEmpty()) { |
| 243 | context.addRect({ }); |
| 244 | return; |
| 245 | } |
| 246 | for (auto inlineRect : inlineBoxRects) |
| 247 | context.addRect(inlineRect); |
| 248 | return; |
| 249 | } |
| 250 | if (auto* curr = firstLegacyInlineBox()) { |
| 251 | for (; curr; curr = curr->nextLineBox()) |
| 252 | context.addRect(FloatRect(curr->topLeft(), curr->size())); |
| 253 | } else |
| 254 | context.addRect(FloatRect()); |
| 255 | } |
| 256 | |
| 257 | class AbsoluteRectsGeneratorContext { |
| 258 | public: |
| 259 | AbsoluteRectsGeneratorContext(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset) |
| 260 | : m_rects(rects) |
| 261 | , m_accumulatedOffset(accumulatedOffset) { } |
| 262 | |
| 263 | void addRect(const FloatRect& rect) |
| 264 | { |
| 265 | LayoutRect adjustedRect = LayoutRect(rect); |
| 266 | adjustedRect.moveBy(m_accumulatedOffset); |
| 267 | m_rects.append(adjustedRect); |
| 268 | } |
| 269 | private: |
| 270 | Vector<LayoutRect>& m_rects; |
| 271 | const LayoutPoint& m_accumulatedOffset; |
| 272 | }; |
| 273 | |
| 274 | void RenderInline::boundingRects(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset) const |
| 275 | { |
| 276 | AbsoluteRectsGeneratorContext context(rects, accumulatedOffset); |
| 277 | generateLineBoxRects(context); |
| 278 | |
| 279 | if (auto* continuation = this->continuation()) { |
| 280 | if (auto* box = dynamicDowncast<RenderBox>(*continuation)) { |
| 281 | continuation->boundingRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box->locationOffset())); |
| 282 | } else |
| 283 | continuation->boundingRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location())); |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | namespace { |
| 288 | |
| 289 | class AbsoluteQuadsGeneratorContext { |
| 290 | public: |
| 291 | AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads) |
| 292 | : m_quads(quads) |
| 293 | , m_geometryMap() |
| 294 | { |
| 295 | m_geometryMap.pushMappingsToAncestor(renderer, nullptr); |
| 296 | } |
| 297 | |
| 298 | void addRect(const FloatRect& rect) |
| 299 | { |
| 300 | m_quads.append(m_geometryMap.absoluteRect(rect)); |
| 301 | } |
| 302 | private: |
| 303 | Vector<FloatQuad>& m_quads; |
| 304 | RenderGeometryMap m_geometryMap; |
| 305 | }; |
| 306 | |
| 307 | } // unnamed namespace |
| 308 | |
| 309 | void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const |
| 310 | { |
| 311 | absoluteQuadsIgnoringContinuation({ }, quads, wasFixed); |
| 312 | if (continuation()) |
| 313 | collectAbsoluteQuadsForContinuation(quads, wasFixed); |
| 314 | } |
| 315 | |
| 316 | void RenderInline::absoluteQuadsIgnoringContinuation(const FloatRect&, Vector<FloatQuad>& quads, bool*) const |
| 317 | { |
| 318 | AbsoluteQuadsGeneratorContext context(this, quads); |
| 319 | generateLineBoxRects(context); |
| 320 | } |
| 321 | |
| 322 | #if PLATFORM(IOS_FAMILY)(defined 1 && 1) |
| 323 | void RenderInline::absoluteQuadsForSelection(Vector<FloatQuad>& quads) const |
| 324 | { |
| 325 | AbsoluteQuadsGeneratorContext context(this, quads); |
| 326 | generateLineBoxRects(context); |
| 327 | } |
| 328 | #endif |
| 329 | |
| 330 | LayoutUnit RenderInline::offsetLeft() const |
| 331 | { |
| 332 | return adjustedPositionRelativeToOffsetParent(firstInlineBoxTopLeft()).x(); |
| 333 | } |
| 334 | |
| 335 | LayoutUnit RenderInline::offsetTop() const |
| 336 | { |
| 337 | return adjustedPositionRelativeToOffsetParent(firstInlineBoxTopLeft()).y(); |
| 338 | } |
| 339 | |
| 340 | LayoutPoint RenderInline::firstInlineBoxTopLeft() const |
| 341 | { |
| 342 | if (auto* lineLayout = LayoutIntegration::LineLayout::containing(*this)) |
| 343 | return lineLayout->firstInlineBoxRect(*this).location(); |
| 344 | if (auto* inlineBox = firstLegacyInlineBox()) |
| 345 | return flooredLayoutPoint(inlineBox->locationIncludingFlipping()); |
| 346 | return { }; |
| 347 | } |
| 348 | |
| 349 | static LayoutUnit computeMargin(const RenderInline* renderer, const Style::MarginEdge& margin, const Style::ZoomFactor& zoomFactor) |
| 350 | { |
| 351 | return Style::evaluateMinimum<LayoutUnit>(margin, [&] ALWAYS_INLINE_LAMBDA__attribute__((__always_inline__)) { |
| 352 | return std::max<LayoutUnit>(0, renderer->containingBlock()->contentBoxLogicalWidth()); |
| 353 | }, zoomFactor); |
| 354 | } |
| 355 | |
| 356 | LayoutUnit RenderInline::marginLeft() const |
| 357 | { |
| 358 | return computeMargin(this, style().marginLeft(), style().usedZoomForLength()); |
| 359 | } |
| 360 | |
| 361 | LayoutUnit RenderInline::marginRight() const |
| 362 | { |
| 363 | return computeMargin(this, style().marginRight(), style().usedZoomForLength()); |
| 364 | } |
| 365 | |
| 366 | LayoutUnit RenderInline::marginTop() const |
| 367 | { |
| 368 | return computeMargin(this, style().marginTop(), style().usedZoomForLength()); |
| 369 | } |
| 370 | |
| 371 | LayoutUnit RenderInline::marginBottom() const |
| 372 | { |
| 373 | return computeMargin(this, style().marginBottom(), style().usedZoomForLength()); |
| 374 | } |
| 375 | |
| 376 | LayoutUnit RenderInline::marginStart(const WritingMode writingMode) const |
| 377 | { |
| 378 | return computeMargin(this, style().marginStart(writingMode), style().usedZoomForLength()); |
| 379 | } |
| 380 | |
| 381 | LayoutUnit RenderInline::marginEnd(const WritingMode writingMode) const |
| 382 | { |
| 383 | return computeMargin(this, style().marginEnd(writingMode), style().usedZoomForLength()); |
| 384 | } |
| 385 | |
| 386 | LayoutUnit RenderInline::marginBefore(const WritingMode writingMode) const |
| 387 | { |
| 388 | return computeMargin(this, style().marginBefore(writingMode), style().usedZoomForLength()); |
| 389 | } |
| 390 | |
| 391 | LayoutUnit RenderInline::marginAfter(const WritingMode writingMode) const |
| 392 | { |
| 393 | return computeMargin(this, style().marginAfter(writingMode), style().usedZoomForLength()); |
| 394 | } |
| 395 | |
| 396 | ASCIILiteral RenderInline::renderName() const |
| 397 | { |
| 398 | if (isRelativelyPositioned()) |
| 399 | return "RenderInline (relative positioned)"_s; |
| 400 | if (isStickilyPositioned()) |
| 401 | return "RenderInline (sticky positioned)"_s; |
| 402 | // FIXME: Temporary hack while the new generated content system is being implemented. |
| 403 | if (isPseudoElement()) |
| 404 | return "RenderInline (generated)"_s; |
| 405 | if (isAnonymous()) |
| 406 | return "RenderInline (generated)"_s; |
| 407 | return "RenderInline"_s; |
| 408 | } |
| 409 | |
| 410 | bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, |
| 411 | const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) |
| 412 | { |
| 413 | ASSERT(layer())((void)0); |
| 414 | if (auto* lineLayout = LayoutIntegration::LineLayout::containing(*this)) |
| 415 | return lineLayout->hitTest(request, result, locationInContainer, accumulatedOffset, hitTestAction, this); |
| 416 | return false; |
| 417 | } |
| 418 | |
| 419 | PositionWithAffinity RenderInline::positionForPoint(const LayoutPoint& point, HitTestSource source, const RenderFragmentContainer* fragment) |
| 420 | { |
| 421 | auto& containingBlock = *this->containingBlock(); |
| 422 | |
| 423 | if (auto* continuation = this->continuation()) { |
| 424 | // Translate the coords from the pre-anonymous block to the post-anonymous block. |
| 425 | LayoutPoint parentBlockPoint = containingBlock.location() + point; |
| 426 | while (continuation) { |
| 427 | RenderBlock* currentBlock = continuation->isInline() ? continuation->containingBlock() : downcast<RenderBlock>(continuation); |
| 428 | if (continuation->isInline() || continuation->firstChild()) |
| 429 | return continuation->positionForPoint(parentBlockPoint - currentBlock->locationOffset(), source, fragment); |
| 430 | continuation = continuation->inlineContinuation(); |
| 431 | } |
| 432 | return RenderBoxModelObject::positionForPoint(point, source, fragment); |
| 433 | } |
| 434 | |
| 435 | return containingBlock.positionForPoint(point, source, fragment); |
| 436 | } |
| 437 | |
| 438 | LayoutUnit RenderInline::innerPaddingBoxWidth() const |
| 439 | { |
| 440 | auto firstInlineBoxPaddingBoxLeft = LayoutUnit { }; |
| 441 | auto lastInlineBoxPaddingBoxRight = LayoutUnit { }; |
| 442 | |
| 443 | if (LayoutIntegration::LineLayout::containing(*this)) { |
| 444 | if (auto inlineBox = InlineIterator::lineLeftmostInlineBoxFor(*this)) { |
| 445 | if (writingMode().isBidiLTR()) { |
| 446 | firstInlineBoxPaddingBoxLeft = inlineBox->logicalLeftIgnoringInlineDirection() + borderStart(); |
| 447 | for (; inlineBox->nextInlineBoxLineRightward(); inlineBox.traverseInlineBoxLineRightward()) { } |
| 448 | ASSERT(inlineBox)((void)0); |
| 449 | lastInlineBoxPaddingBoxRight = inlineBox->logicalRightIgnoringInlineDirection() - borderEnd(); |
| 450 | } else { |
| 451 | lastInlineBoxPaddingBoxRight = inlineBox->logicalRightIgnoringInlineDirection() - borderStart(); |
| 452 | for (; inlineBox->nextInlineBoxLineRightward(); inlineBox.traverseInlineBoxLineRightward()) { } |
| 453 | ASSERT(inlineBox)((void)0); |
| 454 | firstInlineBoxPaddingBoxLeft = inlineBox->logicalLeftIgnoringInlineDirection() + borderEnd(); |
| 455 | } |
| 456 | return std::max(0_lu, lastInlineBoxPaddingBoxRight - firstInlineBoxPaddingBoxLeft); |
| 457 | } |
| 458 | return { }; |
| 459 | } |
| 460 | |
| 461 | auto* firstInlineBox = firstLegacyInlineBox(); |
| 462 | auto* lastInlineBox = lastLegacyInlineBox(); |
| 463 | if (!firstInlineBox || !lastInlineBox) |
| 464 | return { }; |
| 465 | |
| 466 | if (writingMode().isBidiLTR()) { |
| 467 | firstInlineBoxPaddingBoxLeft = firstInlineBox->logicalLeft(); |
| 468 | lastInlineBoxPaddingBoxRight = lastInlineBox->logicalRight(); |
| 469 | } else { |
| 470 | lastInlineBoxPaddingBoxRight = firstInlineBox->logicalRight(); |
| 471 | firstInlineBoxPaddingBoxLeft = lastInlineBox->logicalLeft(); |
| 472 | } |
| 473 | return std::max(0_lu, lastInlineBoxPaddingBoxRight - firstInlineBoxPaddingBoxLeft); |
| 474 | } |
| 475 | |
| 476 | LayoutUnit RenderInline::innerPaddingBoxHeight() const |
| 477 | { |
| 478 | auto innerPaddingBoxLogicalHeight = LayoutUnit { isHorizontalWritingMode() ? linesBoundingBox().height() : linesBoundingBox().width() }; |
| 479 | innerPaddingBoxLogicalHeight -= (borderBefore() + borderAfter()); |
| 480 | return innerPaddingBoxLogicalHeight; |
| 481 | } |
| 482 | |
| 483 | IntRect RenderInline::linesBoundingBox() const |
| 484 | { |
| 485 | if (auto* layout = LayoutIntegration::LineLayout::containing(*this)) { |
| 486 | if (!layoutBox() || !layout->contains(*this)) { |
| 487 | // Repaint may be issued on subtrees during content mutation with newly inserted renderers |
| 488 | // (or we just forgot to initiate layout before querying geometry on stale content after moving inline boxes between blocks). |
| 489 | ASSERT(needsLayout())((void)0); |
| 490 | return { }; |
| 491 | } |
| 492 | if (isRenderSVGInline()) { |
| 493 | // FIXME: Always build the bounding box like this. LineLayouyt::enclosingBorderBoxRectFor does not include |
| 494 | // any post-layout box adjustments. |
| 495 | FloatRect result; |
| 496 | for (auto box = InlineIterator::lineLeftmostInlineBoxFor(*this); box; box.traverseInlineBoxLineRightward()) { |
| 497 | auto rect = box->visualRectIgnoringBlockDirection(); |
| 498 | result.unite(rect); |
| 499 | } |
| 500 | return enclosingIntRect(result); |
| 501 | } |
| 502 | return enclosingIntRect(layout->enclosingBorderBoxRectFor(*this)); |
| 503 | } |
| 504 | |
| 505 | // See <rdar://problem/5289721>, for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been |
| 506 | // unable to reproduce this at all (and consequently unable to figure ot why this is happening). The assert will hopefully catch the problem in debug |
| 507 | // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now. |
| 508 | ASSERT(!firstLegacyInlineBox() == !lastLegacyInlineBox())((void)0); // Either both are null or both exist. |
| 509 | IntRect result; |
| 510 | if (firstLegacyInlineBox() && lastLegacyInlineBox()) { |
| 511 | // Return the width of the minimal left side and the maximal right side. |
| 512 | float logicalLeftSide = 0; |
| 513 | float logicalRightSide = 0; |
| 514 | for (auto* curr = firstLegacyInlineBox(); curr; curr = curr->nextLineBox()) { |
| 515 | if (curr == firstLegacyInlineBox() || curr->logicalLeft() < logicalLeftSide) |
| 516 | logicalLeftSide = curr->logicalLeft(); |
| 517 | if (curr == firstLegacyInlineBox() || curr->logicalRight() > logicalRightSide) |
| 518 | logicalRightSide = curr->logicalRight(); |
| 519 | } |
| 520 | |
| 521 | bool isHorizontal = writingMode().isHorizontal(); |
| 522 | |
| 523 | float x = isHorizontal ? logicalLeftSide : firstLegacyInlineBox()->x(); |
| 524 | float y = isHorizontal ? firstLegacyInlineBox()->y() : logicalLeftSide; |
| 525 | float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLegacyInlineBox()->logicalBottom() - x; |
| 526 | float height = isHorizontal ? lastLegacyInlineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide; |
| 527 | result = enclosingIntRect(FloatRect(x, y, width, height)); |
| 528 | } |
| 529 | |
| 530 | return result; |
| 531 | } |
| 532 | |
| 533 | LayoutRect RenderInline::linesVisualOverflowBoundingBox() const |
| 534 | { |
| 535 | if (auto* layout = LayoutIntegration::LineLayout::containing(*this)) { |
| 536 | if (!layoutBox()) { |
| 537 | // Repaint may be issued on subtrees during content mutation with newly inserted renderers. |
| 538 | ASSERT(needsLayout())((void)0); |
| 539 | return { }; |
| 540 | } |
| 541 | return layout->inkOverflowBoundingBoxRectFor(*this); |
| 542 | } |
| 543 | |
| 544 | if (!firstLegacyInlineBox() || !lastLegacyInlineBox()) |
| 545 | return { }; |
| 546 | |
| 547 | // Return the width of the minimal left side and the maximal right side. |
| 548 | LayoutUnit logicalLeftSide = LayoutUnit::max(); |
| 549 | LayoutUnit logicalRightSide = LayoutUnit::min(); |
| 550 | for (auto* curr = firstLegacyInlineBox(); curr; curr = curr->nextLineBox()) { |
| 551 | logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow()); |
| 552 | logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow()); |
| 553 | } |
| 554 | |
| 555 | const LegacyRootInlineBox& firstRootBox = firstLegacyInlineBox()->root(); |
| 556 | const LegacyRootInlineBox& lastRootBox = lastLegacyInlineBox()->root(); |
| 557 | |
| 558 | LayoutUnit logicalTop = firstLegacyInlineBox()->logicalTopVisualOverflow(firstRootBox.lineTop()); |
| 559 | LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide; |
| 560 | LayoutUnit logicalHeight = lastLegacyInlineBox()->logicalBottomVisualOverflow(lastRootBox.lineBottom()) - logicalTop; |
| 561 | |
| 562 | LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight); |
| 563 | if (!writingMode().isHorizontal()) |
| 564 | rect = rect.transposedRect(); |
| 565 | return rect; |
| 566 | } |
| 567 | |
| 568 | LayoutRect RenderInline::clippedOverflowRect(const RenderLayerModelObject* repaintContainer, VisibleRectContext context) const |
| 569 | { |
| 570 | // Only first-letter renderers are allowed in here during layout. They mutate the tree triggering repaints. |
| 571 | #ifndef NDEBUG1 |
| 572 | auto insideSelfPaintingInlineBox = [&] { |
| 573 | if (hasSelfPaintingLayer()) |
| 574 | return true; |
| 575 | auto* containingBlock = this->containingBlock(); |
| 576 | for (auto* ancestor = this->parent(); ancestor && ancestor != containingBlock; ancestor = ancestor->parent()) { |
| 577 | if (ancestor->hasSelfPaintingLayer()) |
| 578 | return true; |
| 579 | } |
| 580 | return false; |
| 581 | }; |
| 582 | ASSERT_UNUSED(insideSelfPaintingInlineBox, !view().frameView().layoutContext().isPaintOffsetCacheEnabled() || style().pseudoElementType() == PseudoElementType::FirstLetter || insideSelfPaintingInlineBox())((void)insideSelfPaintingInlineBox); |
| 583 | #endif |
| 584 | |
| 585 | auto knownEmpty = [&] { |
| 586 | if (firstLegacyInlineBox()) |
| 587 | return false; |
| 588 | if (continuation()) |
| 589 | return false; |
| 590 | if (LayoutIntegration::LineLayout::containing(*this)) |
| 591 | return false; |
| 592 | return true; |
| 593 | }; |
| 594 | |
| 595 | if (knownEmpty()) |
| 596 | return LayoutRect(); |
| 597 | |
| 598 | auto repaintRect = linesVisualOverflowBoundingBox(); |
| 599 | bool hitRepaintContainer = false; |
| 600 | |
| 601 | // We need to add in the in-flow position offsets of any inlines (including us) up to our |
| 602 | // containing block. |
| 603 | RenderBlock* containingBlock = this->containingBlock(); |
| 604 | for (const RenderElement* inlineFlow = this; inlineFlow; inlineFlow = inlineFlow->parent()) { |
| 605 | auto* renderInline = dynamicDowncast<RenderInline>(*inlineFlow); |
| 606 | if (!renderInline || inlineFlow == containingBlock) |
| 607 | break; |
| 608 | if (inlineFlow == repaintContainer) { |
| 609 | hitRepaintContainer = true; |
| 610 | break; |
| 611 | } |
| 612 | if (inlineFlow->style().hasInFlowPosition() && inlineFlow->hasLayer()) |
| 613 | repaintRect.move(renderInline->layer()->offsetForInFlowPosition()); |
| 614 | } |
| 615 | |
| 616 | LayoutUnit outlineSize { style().usedOutlineSize() }; |
| 617 | repaintRect.inflate(outlineSize); |
| 618 | |
| 619 | if (hitRepaintContainer || !containingBlock) |
| 620 | return repaintRect; |
| 621 | |
| 622 | auto rects = RepaintRects { repaintRect }; |
| 623 | |
| 624 | if (containingBlock->hasNonVisibleOverflow()) |
| 625 | containingBlock->applyCachedClipAndScrollPosition(rects, repaintContainer, context); |
| 626 | |
| 627 | rects = containingBlock->computeRects(rects, repaintContainer, context); |
| 628 | repaintRect = rects.clippedOverflowRect; |
| 629 | |
| 630 | if (outlineSize) { |
| 631 | for (auto& child : childrenOfType<RenderElement>(*this)) |
| 632 | repaintRect.unite(child.rectWithOutlineForRepaint(repaintContainer, outlineSize)); |
| 633 | |
| 634 | if (RenderBoxModelObject* continuation = this->continuation()) { |
| 635 | if (!continuation->isInline() && continuation->parent()) |
| 636 | repaintRect.unite(continuation->rectWithOutlineForRepaint(repaintContainer, outlineSize)); |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | return repaintRect; |
| 641 | } |
| 642 | |
| 643 | auto RenderInline::rectsForRepaintingAfterLayout(const RenderLayerModelObject* repaintContainer, RepaintOutlineBounds) const -> RepaintRects |
| 644 | { |
| 645 | // RepaintOutlineBounds is unused for inlines. |
| 646 | return { clippedOverflowRect(repaintContainer, visibleRectContextForRepaint()) }; |
| 647 | } |
| 648 | |
| 649 | LayoutRect RenderInline::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const |
| 650 | { |
| 651 | LayoutRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); |
| 652 | for (auto& child : childrenOfType<RenderElement>(*this)) |
| 653 | r.unite(child.rectWithOutlineForRepaint(repaintContainer, outlineWidth)); |
| 654 | return r; |
| 655 | } |
| 656 | |
| 657 | auto RenderInline::computeVisibleRectsUsingPaintOffset(const RepaintRects& rects) const -> RepaintRects |
| 658 | { |
| 659 | auto adjustedRects = rects; |
| 660 | auto* layoutState = view().frameView().layoutContext().layoutState(); |
| 661 | if (style().hasInFlowPosition() && layer()) |
| 662 | adjustedRects.move(layer()->offsetForInFlowPosition()); |
| 663 | adjustedRects.move(layoutState->paintOffset()); |
| 664 | if (layoutState->isClipped()) |
| 665 | adjustedRects.clippedOverflowRect.intersect(layoutState->clipRect()); |
| 666 | return adjustedRects; |
| 667 | } |
| 668 | |
| 669 | auto RenderInline::computeVisibleRectsInContainer(const RepaintRects& rects, const RenderLayerModelObject* container, VisibleRectContext context) const -> std::optional<RepaintRects> |
| 670 | { |
| 671 | // Repaint offset cache is only valid for root-relative repainting |
| 672 | if (view().frameView().layoutContext().isPaintOffsetCacheEnabled() && !container && !context.options.contains(VisibleRectContext::Option::UseEdgeInclusiveIntersection)) |
| 673 | return computeVisibleRectsUsingPaintOffset(rects); |
| 674 | |
| 675 | if (container == this) |
| 676 | return rects; |
| 677 | |
| 678 | bool containerSkipped; |
| 679 | RenderElement* localContainer = this->container(container, containerSkipped); |
| 680 | if (!localContainer) |
| 681 | return rects; |
| 682 | |
| 683 | auto adjustedRects = rects; |
| 684 | if (style().hasInFlowPosition() && layer()) { |
| 685 | // Apply the in-flow position offset when invalidating a rectangle. The layer |
| 686 | // is translated, but the render box isn't, so we need to do this to get the |
| 687 | // right dirty rect. Since this is called from RenderObject::setStyle, the relative or sticky position |
| 688 | // flag on the RenderObject has been cleared, so use the one on the style(). |
| 689 | auto offsetForInFlowPosition = layer()->offsetForInFlowPosition(); |
| 690 | adjustedRects.move(offsetForInFlowPosition); |
| 691 | } |
| 692 | |
| 693 | if (localContainer->hasNonVisibleOverflow()) { |
| 694 | // FIXME: Respect the value of context.options. |
| 695 | SetForScope change(context.options, context.options | VisibleRectContext::Option::ApplyCompositedContainerScrolls); |
| 696 | bool isEmpty = !downcast<RenderLayerModelObject>(*localContainer).applyCachedClipAndScrollPosition(adjustedRects, container, context); |
| 697 | if (isEmpty) { |
| 698 | if (context.options.contains(VisibleRectContext::Option::UseEdgeInclusiveIntersection)) |
| 699 | return std::nullopt; |
| 700 | return adjustedRects; |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | if (containerSkipped) { |
| 705 | // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. |
| 706 | auto containerOffset = container->offsetFromAncestorContainer(*localContainer); |
| 707 | adjustedRects.move(-containerOffset); |
| 708 | return adjustedRects; |
| 709 | } |
| 710 | |
| 711 | return localContainer->computeVisibleRectsInContainer(adjustedRects, container, context); |
| 712 | } |
| 713 | |
| 714 | LayoutSize RenderInline::offsetFromContainer(const RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const |
| 715 | { |
| 716 | ASSERT(&container == this->container())((void)0); |
| 717 | |
| 718 | LayoutSize offset; |
| 719 | if (isInFlowPositioned()) |
| 720 | offset += offsetForInFlowPosition(); |
| 721 | |
| 722 | if (auto* box = dynamicDowncast<RenderBox>(container)) |
| 723 | offset -= toLayoutSize(box->scrollPosition()); |
| 724 | |
| 725 | if (offsetDependsOnPoint) |
| 726 | *offsetDependsOnPoint = (is<RenderBox>(container) && container.writingMode().isBlockFlipped()) || is<RenderFragmentedFlow>(container); |
| 727 | |
| 728 | return offset; |
| 729 | } |
| 730 | |
| 731 | void RenderInline::mapLocalToContainer(const RenderLayerModelObject* ancestorContainer, TransformState& transformState, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const |
| 732 | { |
| 733 | if (ancestorContainer == this) |
| 734 | return; |
| 735 | |
| 736 | if (view().frameView().layoutContext().isPaintOffsetCacheEnabled() && !ancestorContainer) { |
| 737 | auto* layoutState = view().frameView().layoutContext().layoutState(); |
| 738 | LayoutSize offset = layoutState->paintOffset(); |
| 739 | if (style().hasInFlowPosition() && layer()) |
| 740 | offset += layer()->offsetForInFlowPosition(); |
| 741 | transformState.move(offset); |
| 742 | return; |
| 743 | } |
| 744 | |
| 745 | bool containerSkipped; |
| 746 | RenderElement* container = this->container(ancestorContainer, containerSkipped); |
| 747 | if (!container) |
| 748 | return; |
| 749 | |
| 750 | if (mode.contains(ApplyContainerFlip)) { |
| 751 | if (CheckedPtr box = dynamicDowncast<RenderBox>(*container)) { |
| 752 | if (container->writingMode().isBlockFlipped()) { |
| 753 | LayoutPoint centerPoint(transformState.mappedPoint()); |
| 754 | transformState.move(box->flipForWritingMode(centerPoint) - centerPoint); |
| 755 | } |
| 756 | mode.remove(ApplyContainerFlip); |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(transformState.mappedPoint())); |
| 761 | |
| 762 | pushOntoTransformState(transformState, mode, ancestorContainer, container, containerOffset, containerSkipped); |
| 763 | if (containerSkipped) |
| 764 | return; |
| 765 | |
| 766 | container->mapLocalToContainer(ancestorContainer, transformState, mode, wasFixed); |
| 767 | } |
| 768 | |
| 769 | const RenderElement* RenderInline::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const |
| 770 | { |
| 771 | ASSERT(ancestorToStopAt != this)((void)0); |
| 772 | |
| 773 | bool ancestorSkipped; |
| 774 | RenderElement* container = this->container(ancestorToStopAt, ancestorSkipped); |
| 775 | if (!container) |
| 776 | return nullptr; |
| 777 | |
| 778 | pushOntoGeometryMap(geometryMap, ancestorToStopAt, container, ancestorSkipped); |
| 779 | |
| 780 | return ancestorSkipped ? ancestorToStopAt : container; |
| 781 | } |
| 782 | |
| 783 | void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point) const |
| 784 | { |
| 785 | if (result.innerNode()) |
| 786 | return; |
| 787 | |
| 788 | LayoutPoint localPoint(point); |
| 789 | if (RefPtr node = nodeForHitTest()) { |
| 790 | if (isContinuation()) { |
| 791 | // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space |
| 792 | // of the principal renderer's containing block. This will end up being the innerNonSharedNode. |
| 793 | auto* firstBlock = node->renderer()->containingBlock(); |
| 794 | localPoint.moveBy(containingBlock()->location() - firstBlock->locationOffset()); |
| 795 | } |
| 796 | |
| 797 | result.setInnerNode(node.get()); |
| 798 | if (!result.innerNonSharedNode()) |
| 799 | result.setInnerNonSharedNode(node.get()); |
| 800 | result.setPseudoElementIdentifier(style().pseudoElementIdentifier()); |
| 801 | result.setLocalPoint(localPoint); |
| 802 | } |
| 803 | } |
| 804 | |
| 805 | void RenderInline::deleteLegacyLineBoxes() |
| 806 | { |
| 807 | m_legacyLineBoxes.deleteLineBoxes(); |
| 808 | } |
| 809 | |
| 810 | std::unique_ptr<LegacyInlineFlowBox> RenderInline::createInlineFlowBox() |
| 811 | { |
| 812 | return makeUnique<LegacyInlineFlowBox>(*this); |
| 813 | } |
| 814 | |
| 815 | LegacyInlineFlowBox* RenderInline::createAndAppendInlineFlowBox() |
| 816 | { |
| 817 | auto newFlowBox = createInlineFlowBox(); |
| 818 | auto flowBox = newFlowBox.get(); |
| 819 | m_legacyLineBoxes.appendLineBox(WTF::move(newFlowBox)); |
| 820 | return flowBox; |
| 821 | } |
| 822 | |
| 823 | LayoutSize RenderInline::offsetForInFlowPositionedInline(const RenderBox* child) const |
| 824 | { |
| 825 | // FIXME: This function isn't right with mixed writing modes. |
| 826 | if (!isInFlowPositioned()) { |
| 827 | ASSERT_NOT_REACHED()((void)0); |
| 828 | return { }; |
| 829 | } |
| 830 | |
| 831 | if (!hasLayer()) { |
| 832 | // It looks like we are inflow positioned but no layer created yet. It essentially means we don't have an position offset yet. |
| 833 | return { }; |
| 834 | } |
| 835 | |
| 836 | // When we have an enclosing relpositioned inline, we need to add in the offset of the first line |
| 837 | // box from the rest of the content, but only in the cases where we know we're positioned |
| 838 | // relative to the inline itself. |
| 839 | auto inlinePosition = layer()->staticInlinePosition(); |
| 840 | auto blockPosition = layer()->staticBlockPosition(); |
| 841 | if (auto* inlineBox = firstLegacyInlineBox()) { |
| 842 | inlinePosition = LayoutUnit::fromFloatRound(inlineBox->logicalLeft()); |
| 843 | blockPosition = inlineBox->logicalTop(); |
| 844 | } else if (LayoutIntegration::LineLayout::containing(*this)) { |
| 845 | if (!layoutBox()) { |
| 846 | // Repaint may be issued on subtrees during content mutation with newly inserted renderers. |
| 847 | ASSERT(needsLayout())((void)0); |
| 848 | return { }; |
| 849 | } |
| 850 | if (auto inlineBox = InlineIterator::lineLeftmostInlineBoxFor(*this)) { |
| 851 | inlinePosition = LayoutUnit::fromFloatRound(inlineBox->logicalLeftIgnoringInlineDirection()); |
| 852 | blockPosition = inlineBox->logicalTop(); |
| 853 | } else if (auto* blockContainer = containingBlock()) { |
| 854 | // This must be a block with no in-flow content e.g. <div><span><abs pos box></span></div> where we don't construct any display box at all. |
| 855 | auto contentBoxLocation = blockContainer->contentBoxLocation(); |
| 856 | inlinePosition = contentBoxLocation.x(); |
| 857 | blockPosition = contentBoxLocation.y(); |
| 858 | } |
| 859 | } |
| 860 | |
| 861 | // Per http://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width an absolute positioned box with a static position |
| 862 | // should locate itself as though it is a normal flow box in relation to its containing block. |
| 863 | LayoutSize logicalOffset; |
| 864 | if (!child->style().hasStaticInlinePosition(writingMode().isHorizontal()) |
| 865 | || !child->style().positionArea().isNone() || child->style().justifySelf().isAnchorCenter()) |
| 866 | logicalOffset.setWidth(inlinePosition); |
| 867 | |
| 868 | if (!child->style().hasStaticBlockPosition(writingMode().isHorizontal()) |
| 869 | || !child->style().positionArea().isNone() || child->style().alignSelf().isAnchorCenter()) |
| 870 | logicalOffset.setHeight(blockPosition); |
| 871 | |
| 872 | return writingMode().isHorizontal() ? logicalOffset : logicalOffset.transposedSize(); |
| 873 | } |
| 874 | |
| 875 | void RenderInline::imageChanged(WrappedImagePtr image, const IntRect*) |
| 876 | { |
| 877 | if (!parent()) |
| 878 | return; |
| 879 | |
| 880 | bool isNonEmpty; |
| 881 | RefPtr styleImage = Style::findLayerUsedImage(style().backgroundLayers(), image, isNonEmpty); |
| 882 | if (styleImage && isNonEmpty) { |
| 883 | if (auto styleable = Styleable::fromRenderer(*this)) |
| 884 | protect(document())->didLoadImage(protect(styleable->element).get(), styleImage->cachedImage()); |
Call argument is uncounted and unsafe | |
| 885 | } |
| 886 | |
| 887 | // FIXME: We can do better. |
| 888 | repaint(); |
| 889 | } |
| 890 | |
| 891 | namespace { |
| 892 | class AbsoluteRectsIgnoringEmptyGeneratorContext : public AbsoluteRectsGeneratorContext { |
| 893 | public: |
| 894 | AbsoluteRectsIgnoringEmptyGeneratorContext(Vector<LayoutRect>& rects, const LayoutPoint& accumulatedOffset) |
| 895 | : AbsoluteRectsGeneratorContext(rects, accumulatedOffset) { } |
| 896 | |
| 897 | void addRect(const FloatRect& rect) |
| 898 | { |
| 899 | if (!rect.isEmpty()) |
| 900 | AbsoluteRectsGeneratorContext::addRect(rect); |
| 901 | } |
| 902 | }; |
| 903 | } // unnamed namespace |
| 904 | |
| 905 | void RenderInline::collectLineBoxRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset) const |
| 906 | { |
| 907 | AbsoluteRectsIgnoringEmptyGeneratorContext context(rects, additionalOffset); |
| 908 | generateLineBoxRects(context); |
| 909 | } |
| 910 | |
| 911 | bool isEmptyInline(const RenderInline& renderer) |
| 912 | { |
| 913 | for (auto& current : childrenOfType<RenderObject>(renderer)) { |
| 914 | if (current.isFloatingOrOutOfFlowPositioned()) |
| 915 | continue; |
| 916 | if (auto* text = dynamicDowncast<RenderText>(current)) { |
| 917 | if (!text->containsOnlyCollapsibleWhitespace()) |
| 918 | return false; |
| 919 | continue; |
| 920 | } |
| 921 | auto* renderInline = dynamicDowncast<RenderInline>(current); |
| 922 | if (!renderInline || !isEmptyInline(*renderInline)) |
| 923 | return false; |
| 924 | } |
| 925 | return true; |
| 926 | } |
| 927 | |
| 928 | bool RenderInline::requiresLayer() const |
| 929 | { |
| 930 | return isInFlowPositioned() |
| 931 | || createsGroup() |
| 932 | || hasClipPath() |
| 933 | || style().willChange().canCreateStackingContext() |
| 934 | || hasRunningAcceleratedAnimations() |
| 935 | || requiresRenderingConsolidationForViewTransition(); |
| 936 | } |
| 937 | |
| 938 | } // namespace WebCore |