| File: | Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/rendering/LegacyLineLayout.cpp |
| Warning: | line 602, column 5 A function 'layoutContext' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * Copyright (C) 2000 Lars Knoll (knoll@kde.org) |
| 3 | * Copyright (C) 2003-2023 Apple Inc. All right reserved. |
| 4 | * Copyright (C) 2010 Google Inc. All rights reserved. |
| 5 | * Copyright (C) 2013 ChangSeok Oh <shivamidow@gmail.com> |
| 6 | * Copyright (C) 2013 Adobe Systems Inc. All right reserved. |
| 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 "LegacyLineLayout.h" |
| 27 | |
| 28 | #include "AXObjectCache.h" |
| 29 | #include "BidiResolver.h" |
| 30 | #include "BreakingContext.h" |
| 31 | #include "DocumentView.h" |
| 32 | #include "FloatingObjects.h" |
| 33 | #include "InlineIteratorBoxInlines.h" |
| 34 | #include "InlineIteratorTextBox.h" |
| 35 | #include "InlineTextBoxStyle.h" |
| 36 | #include "InlineWalker.h" |
| 37 | #include "LegacyInlineIterator.h" |
| 38 | #include "LegacyInlineTextBox.h" |
| 39 | #include "LineInfo.h" |
| 40 | #include "LineInlineHeaders.h" |
| 41 | #include "Logging.h" |
| 42 | #include "RenderBlockFlowInlines.h" |
| 43 | #include "RenderFragmentContainer.h" |
| 44 | #include "RenderFragmentedFlow.h" |
| 45 | #include "RenderLayoutState.h" |
| 46 | #include "RenderLineBreak.h" |
| 47 | #include "RenderObjectInlines.h" |
| 48 | #include "RenderSVGText.h" |
| 49 | #include "RenderView.h" |
| 50 | #include "SVGElementTypeHelpers.h" |
| 51 | #include "SVGRootInlineBox.h" |
| 52 | #include "Settings.h" |
| 53 | #include <wtf/StdLibExtras.h> |
| 54 | #include <wtf/TZoneMallocInlines.h> |
| 55 | |
| 56 | namespace WebCore { |
| 57 | |
| 58 | WTF_MAKE_TZONE_ALLOCATED_IMPL(LegacyLineLayout)::bmalloc::api::HeapRef LegacyLineLayout::s_heapRef; const TZoneSpecification LegacyLineLayout::s_heapSpec = { &LegacyLineLayout::s_heapRef , TZoneSpecification::encodeSize<LegacyLineLayout>(), TZoneSpecification ::encodeAlignment<LegacyLineLayout>(), TZoneSpecification ::encodeCategory<LegacyLineLayout>(), ::bmalloc::api::compactAllocationMode <LegacyLineLayout>(), TZoneSpecification::encodeDescriptor <LegacyLineLayout>(), }; void* LegacyLineLayout::operatorNewSlow (size_t size) { if constexpr (::bmalloc::api::compactAllocationMode <LegacyLineLayout>() == CompactAllocationMode::Compact) return ::bmalloc::api::tzoneAllocateCompactSlow(size, s_heapSpec ); return ::bmalloc::api::tzoneAllocateNonCompactSlow(size, s_heapSpec ); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__ ((unused)) = int; |
| 59 | |
| 60 | LegacyLineLayout::LegacyLineLayout(RenderBlockFlow& flow) |
| 61 | : m_flow(flow) |
| 62 | { |
| 63 | } |
| 64 | |
| 65 | LegacyLineLayout::~LegacyLineLayout() |
| 66 | { |
| 67 | deleteLegacyRootBox(true); |
| 68 | } |
| 69 | |
| 70 | static void determineDirectionality(TextDirection& dir, LegacyInlineIterator iter) |
| 71 | { |
| 72 | while (!iter.atEnd()) { |
| 73 | if (iter.atParagraphSeparator()) |
| 74 | return; |
| 75 | if (char16_t current = iter.current()) { |
| 76 | UCharDirection charDirection = u_charDirection(current); |
| 77 | if (charDirection == U_LEFT_TO_RIGHT) { |
| 78 | dir = TextDirection::LTR; |
| 79 | return; |
| 80 | } |
| 81 | if (charDirection == U_RIGHT_TO_LEFT || charDirection == U_RIGHT_TO_LEFT_ARABIC) { |
| 82 | dir = TextDirection::RTL; |
| 83 | return; |
| 84 | } |
| 85 | } |
| 86 | iter.increment(); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | inline std::unique_ptr<BidiRun> createRun(int start, int end, RenderObject& obj, InlineBidiResolver& resolver) |
| 91 | { |
| 92 | return makeUnique<BidiRun>(start, end, obj, resolver.context(), resolver.dir()); |
| 93 | } |
| 94 | |
| 95 | bool LegacyLineLayout::shouldSkipCreatingRunsForObject(RenderObject& object) |
| 96 | { |
| 97 | if (is<RenderText>(object)) |
| 98 | return false; |
| 99 | auto& renderElement = downcast<RenderElement>(object); |
| 100 | if (renderElement.isFloating()) |
| 101 | return true; |
| 102 | if (renderElement.isOutOfFlowPositioned() && !renderElement.style().originalDisplay().isInlineType() && !renderElement.container()->isRenderInline()) |
| 103 | return true; |
| 104 | return false; |
| 105 | } |
| 106 | |
| 107 | void LegacyLineLayout::appendRunsForObject(BidiRunList<BidiRun>* runs, int start, int end, RenderObject& obj, InlineBidiResolver& resolver) |
| 108 | { |
| 109 | if (start > end || shouldSkipCreatingRunsForObject(obj)) |
| 110 | return; |
| 111 | |
| 112 | LineWhitespaceCollapsingState& lineWhitespaceCollapsingState = resolver.whitespaceCollapsingState(); |
| 113 | bool haveNextTransition = (lineWhitespaceCollapsingState.currentTransition() < lineWhitespaceCollapsingState.numTransitions()); |
| 114 | LegacyInlineIterator nextTransition; |
| 115 | if (haveNextTransition) |
| 116 | nextTransition = lineWhitespaceCollapsingState.transitions()[lineWhitespaceCollapsingState.currentTransition()]; |
| 117 | if (lineWhitespaceCollapsingState.betweenTransitions()) { |
| 118 | if (!haveNextTransition || (&obj != nextTransition.renderer())) |
| 119 | return; |
| 120 | // This is a new start point. Stop ignoring objects and |
| 121 | // adjust our start. |
| 122 | start = nextTransition.offset(); |
| 123 | lineWhitespaceCollapsingState.incrementCurrentTransition(); |
| 124 | if (start < end) { |
| 125 | appendRunsForObject(runs, start, end, obj, resolver); |
| 126 | return; |
| 127 | } |
| 128 | } else { |
| 129 | if (!haveNextTransition || (&obj != nextTransition.renderer())) { |
| 130 | if (runs) |
| 131 | runs->appendRun(createRun(start, end, obj, resolver)); |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | // An end transition has been encountered within our object. We need to append a run with our endpoint. |
| 136 | if (static_cast<int>(nextTransition.offset() + 1) <= end) { |
| 137 | lineWhitespaceCollapsingState.incrementCurrentTransition(); |
| 138 | // The end of the line is before the object we're inspecting. Skip everything and return |
| 139 | if (nextTransition.refersToEndOfPreviousNode()) |
| 140 | return; |
| 141 | if (static_cast<int>(nextTransition.offset() + 1) > start && runs) |
| 142 | runs->appendRun(createRun(start, nextTransition.offset() + 1, obj, resolver)); |
| 143 | appendRunsForObject(runs, nextTransition.offset() + 1, end, obj, resolver); |
| 144 | } else if (runs) |
| 145 | runs->appendRun(createRun(start, end, obj, resolver)); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | std::unique_ptr<LegacyRootInlineBox> LegacyLineLayout::createRootInlineBox() |
| 150 | { |
| 151 | if (CheckedPtr svgText = dynamicDowncast<RenderSVGText>(m_flow)) { |
| 152 | auto box = makeUnique<SVGRootInlineBox>(*svgText); |
| 153 | box->setHasVirtualLogicalHeight(); |
| 154 | return box; |
| 155 | } |
| 156 | |
| 157 | return makeUnique<LegacyRootInlineBox>(m_flow); |
| 158 | } |
| 159 | |
| 160 | LegacyRootInlineBox* LegacyLineLayout::createAndAppendRootInlineBox() |
| 161 | { |
| 162 | m_legacyRootInlineBox = createRootInlineBox(); |
| 163 | if (AXObjectCache::accessibilityEnabled()) [[unlikely]] { |
| 164 | if (AXObjectCache* cache = m_flow.document().existingAXObjectCache()) |
| 165 | cache->deferRecomputeIsIgnored(m_flow.element()); |
| 166 | } |
| 167 | |
| 168 | return m_legacyRootInlineBox.get(); |
| 169 | } |
| 170 | |
| 171 | LegacyInlineBox* LegacyLineLayout::createInlineBoxForRenderer(RenderObject* renderer) |
| 172 | { |
| 173 | if (renderer == &m_flow) |
| 174 | return createAndAppendRootInlineBox(); |
| 175 | |
| 176 | if (auto* textRenderer = dynamicDowncast<RenderSVGInlineText>(*renderer)) |
| 177 | return textRenderer->createInlineTextBox(); |
| 178 | |
| 179 | if (auto* renderInline = dynamicDowncast<RenderInline>(*renderer)) |
| 180 | return renderInline->createAndAppendInlineFlowBox(); |
| 181 | |
| 182 | ASSERT_NOT_REACHED()((void)0); |
| 183 | return nullptr; |
| 184 | } |
| 185 | |
| 186 | static inline void dirtyLineBoxesForRenderer(RenderObject& renderer) |
| 187 | { |
| 188 | if (CheckedPtr renderText = dynamicDowncast<RenderSVGInlineText>(renderer)) |
| 189 | renderText->deleteLegacyLineBoxes(); |
| 190 | else if (CheckedPtr renderInline = dynamicDowncast<RenderInline>(renderer)) |
| 191 | renderInline->deleteLegacyLineBoxes(); |
| 192 | } |
| 193 | |
| 194 | static bool NODELETE[[clang::annotate_type("webkit.nodelete")]] parentIsConstructedOrHaveNext(LegacyInlineFlowBox* parentBox) |
| 195 | { |
| 196 | do { |
| 197 | if (parentBox->isConstructed() || parentBox->nextOnLine()) |
| 198 | return true; |
| 199 | parentBox = parentBox->parent(); |
| 200 | } while (parentBox); |
| 201 | return false; |
| 202 | } |
| 203 | |
| 204 | LegacyInlineFlowBox* LegacyLineLayout::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, LegacyInlineBox* childBox) |
| 205 | { |
| 206 | // See if we have an unconstructed line box for this object that is also |
| 207 | // the last item on the line. |
| 208 | unsigned lineDepth = 1; |
| 209 | LegacyInlineFlowBox* parentBox = nullptr; |
| 210 | LegacyInlineFlowBox* result = nullptr; |
| 211 | do { |
| 212 | RenderInline* inlineFlow = obj != &m_flow ? &downcast<RenderInline>(*obj) : nullptr; |
| 213 | |
| 214 | // Get the last box we made for this render object. |
| 215 | parentBox = inlineFlow ? inlineFlow->lastLegacyInlineBox() : downcast<RenderBlockFlow>(*obj).legacyRootBox(); |
| 216 | |
| 217 | // If this box or its ancestor is constructed then it is from a previous line, and we need |
| 218 | // to make a new box for our line. If this box or its ancestor is unconstructed but it has |
| 219 | // something following it on the line, then we know we have to make a new box |
| 220 | // as well. In this situation our inline has actually been split in two on |
| 221 | // the same line (this can happen with very fancy language mixtures). |
| 222 | bool constructedNewBox = false; |
| 223 | bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox); |
| 224 | if (!canUseExistingParentBox) { |
| 225 | // We need to make a new box for this render object. Once |
| 226 | // made, we need to place it at the end of the current line. |
| 227 | LegacyInlineBox* newBox = createInlineBoxForRenderer(obj); |
| 228 | parentBox = downcast<LegacyInlineFlowBox>(newBox); |
| 229 | parentBox->setIsFirstLine(lineInfo.isFirstLine()); |
| 230 | parentBox->setIsHorizontal(m_flow.isHorizontalWritingMode()); |
| 231 | constructedNewBox = true; |
| 232 | } |
| 233 | |
| 234 | if (constructedNewBox || canUseExistingParentBox) { |
| 235 | if (!result) |
| 236 | result = parentBox; |
| 237 | |
| 238 | // If we have hit the block itself, then |box| represents the root |
| 239 | // inline box for the line, and it doesn't have to be appended to any parent |
| 240 | // inline. |
| 241 | if (childBox) |
| 242 | parentBox->addToLine(childBox); |
| 243 | |
| 244 | if (!constructedNewBox || obj == &m_flow) |
| 245 | break; |
| 246 | |
| 247 | childBox = parentBox; |
| 248 | } |
| 249 | |
| 250 | // If we've exceeded our line depth, then jump straight to the root and skip all the remaining |
| 251 | // intermediate inline flows. |
| 252 | obj = (++lineDepth >= cMaxLineDepth) ? &m_flow : obj->parent(); |
| 253 | |
| 254 | } while (true); |
| 255 | |
| 256 | return result; |
| 257 | } |
| 258 | |
| 259 | LegacyRootInlineBox* LegacyLineLayout::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo) |
| 260 | { |
| 261 | if (legacyRootBox()) { |
| 262 | // Refuse to create multiple lines for svg content. There should not need to be more than one. |
| 263 | ASSERT_NOT_REACHED()((void)0); |
| 264 | return nullptr; |
| 265 | } |
| 266 | |
| 267 | ASSERT(bidiRuns.firstRun())((void)0); |
| 268 | LegacyInlineFlowBox* parentBox = 0; |
| 269 | for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) { |
| 270 | if (lineInfo.isEmpty()) |
| 271 | continue; |
| 272 | |
| 273 | LegacyInlineBox* box = createInlineBoxForRenderer(&r->renderer()); |
| 274 | r->setBox(box); |
| 275 | |
| 276 | // If we have no parent box yet, or if the run is not simply a sibling, |
| 277 | // then we need to construct inline boxes as necessary to properly enclose the |
| 278 | // run's inline box. Segments can only be siblings at the root level, as |
| 279 | // they are positioned separately. |
| 280 | if (!parentBox || &parentBox->renderer() != r->renderer().parent()) { |
| 281 | // Create new inline boxes all the way back to the appropriate insertion point. |
| 282 | RenderObject* parentToUse = r->renderer().parent(); |
| 283 | parentBox = createLineBoxes(parentToUse, lineInfo, box); |
| 284 | } else { |
| 285 | // Append the inline box to this line. |
| 286 | parentBox->addToLine(box); |
| 287 | } |
| 288 | |
| 289 | box->setBidiLevel(r->level()); |
| 290 | |
| 291 | if (auto* textBox = dynamicDowncast<LegacyInlineTextBox>(*box)) { |
| 292 | textBox->setStart(r->m_start); |
| 293 | textBox->setLen(r->m_stop - r->m_start); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | // We should have a root inline box. It should be unconstructed and |
| 298 | // be the last continuation of our line list. |
| 299 | ASSERT(legacyRootBox() && !legacyRootBox()->isConstructed())((void)0); |
| 300 | |
| 301 | // Now mark the line boxes as being constructed. |
| 302 | legacyRootBox()->setConstructed(); |
| 303 | return legacyRootBox(); |
| 304 | } |
| 305 | |
| 306 | void LegacyLineLayout::removeInlineBox(BidiRun& run, const LegacyRootInlineBox& rootLineBox) const |
| 307 | { |
| 308 | auto* inlineBox = run.box(); |
| 309 | #if ASSERT_ENABLED0 |
| 310 | auto* inlineParent = inlineBox->parent(); |
| 311 | while (inlineParent && inlineParent != &rootLineBox) { |
| 312 | ASSERT(!inlineParent->isDirty())((void)0); |
| 313 | inlineParent = inlineParent->parent(); |
| 314 | } |
| 315 | ASSERT(!rootLineBox.isDirty())((void)0); |
| 316 | #endif |
| 317 | auto* parent = inlineBox->parent(); |
| 318 | inlineBox->removeFromParent(); |
| 319 | |
| 320 | auto& renderer = run.renderer(); |
| 321 | if (CheckedPtr textRenderer = dynamicDowncast<RenderSVGInlineText>(renderer)) |
| 322 | textRenderer->removeTextBox(downcast<LegacyInlineTextBox>(*inlineBox)); |
| 323 | delete inlineBox; |
| 324 | run.setBox(nullptr); |
| 325 | // removeFromParent() unnecessarily dirties the ancestor subtree. |
| 326 | auto* ancestor = parent; |
| 327 | while (ancestor) { |
| 328 | ancestor->markDirty(false); |
| 329 | if (ancestor == &rootLineBox) |
| 330 | break; |
| 331 | ancestor = ancestor->parent(); |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | void LegacyLineLayout::removeEmptyTextBoxesAndUpdateVisualReordering(LegacyRootInlineBox* lineBox, BidiRun* firstRun) |
| 336 | { |
| 337 | for (auto* run = firstRun; run; run = run->next()) { |
| 338 | if (auto* inlineTextBox = dynamicDowncast<LegacyInlineTextBox>(*run->box())) { |
| 339 | auto& renderer = inlineTextBox->renderer(); |
| 340 | if (!inlineTextBox->hasTextContent()) { |
| 341 | removeInlineBox(*run, *lineBox); |
| 342 | continue; |
| 343 | } |
| 344 | if (!inlineTextBox->isLeftToRightDirection()) |
| 345 | renderer.setNeedsVisualReordering(); |
| 346 | } |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | static inline void notifyResolverToResumeInIsolate(InlineBidiResolver& resolver, const RenderInline* root, RenderObject* startObject) |
| 351 | { |
| 352 | if (root != startObject) { |
| 353 | RenderObject* parent = startObject->parent(); |
| 354 | notifyResolverToResumeInIsolate(resolver, root, parent); |
| 355 | notifyObserverEnteredObject(&resolver, startObject); |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | static inline void setUpResolverToResumeInIsolate(InlineBidiResolver& resolver, InlineBidiResolver& topResolver, BidiRun& isolatedRun, const RenderInline* root, RenderObject* startObject) |
| 360 | { |
| 361 | // Set up m_whitespaceCollapsingState |
| 362 | resolver.whitespaceCollapsingState() = topResolver.whitespaceCollapsingState(); |
| 363 | resolver.whitespaceCollapsingState().setCurrentTransition(topResolver.whitespaceCollapsingTransitionForIsolatedRun(isolatedRun)); |
| 364 | |
| 365 | // Set up m_nestedIsolateCount |
| 366 | notifyResolverToResumeInIsolate(resolver, root, startObject); |
| 367 | } |
| 368 | |
| 369 | // FIXME: BidiResolver should have this logic. |
| 370 | static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const LegacyInlineIterator& endOfRuns, VisualDirectionOverride override) |
| 371 | { |
| 372 | // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead |
| 373 | // of the resolver owning the runs. |
| 374 | ASSERT(&topResolver.runs() == &bidiRuns)((void)0); |
| 375 | ASSERT(topResolver.position() != endOfRuns)((void)0); |
| 376 | topResolver.createBidiRunsForLine(endOfRuns, override); |
| 377 | |
| 378 | while (!topResolver.isolatedRuns().isEmpty()) { |
| 379 | // It does not matter which order we resolve the runs as long as we resolve them all. |
| 380 | auto isolatedRun = WTF::move(topResolver.isolatedRuns().last()); |
| 381 | topResolver.isolatedRuns().removeLast(); |
| 382 | |
| 383 | RenderObject& startObject = isolatedRun.object; |
| 384 | |
| 385 | // Only inlines make sense with unicode-bidi: isolate (blocks are already isolated). |
| 386 | // FIXME: Because enterIsolate is not passed a RenderObject, we have to crawl up the |
| 387 | // tree to see which parent inline is the isolate. We could change enterIsolate |
| 388 | // to take a RenderObject and do this logic there, but that would be a layering |
| 389 | // violation for BidiResolver (which knows nothing about RenderObject). |
| 390 | RenderInline* isolatedInline = downcast<RenderInline>(highestContainingIsolateWithinRoot(startObject, &isolatedRun.root)); |
| 391 | ASSERT(isolatedInline)((void)0); |
| 392 | |
| 393 | InlineBidiResolver isolatedResolver; |
| 394 | auto unicodeBidi = isolatedInline->style().unicodeBidi(); |
| 395 | TextDirection direction; |
| 396 | if (unicodeBidi == UnicodeBidi::Plaintext) |
| 397 | determineDirectionality(direction, LegacyInlineIterator(isolatedInline, &isolatedRun.object, 0)); |
| 398 | else { |
| 399 | ASSERT(unicodeBidi == UnicodeBidi::Isolate || unicodeBidi == UnicodeBidi::IsolateOverride)((void)0); |
| 400 | direction = isolatedInline->writingMode().bidiDirection(); |
| 401 | } |
| 402 | isolatedResolver.setStatus(BidiStatus(direction, isOverride(unicodeBidi))); |
| 403 | |
| 404 | setUpResolverToResumeInIsolate(isolatedResolver, topResolver, isolatedRun.runToReplace, isolatedInline, &startObject); |
| 405 | |
| 406 | // The starting position is the beginning of the first run within the isolate that was identified |
| 407 | // during the earlier call to createBidiRunsForLine. This can be but is not necessarily the |
| 408 | // first run within the isolate. |
| 409 | LegacyInlineIterator iter = LegacyInlineIterator(isolatedInline, &startObject, isolatedRun.position); |
| 410 | isolatedResolver.setPositionIgnoringNestedIsolates(iter); |
| 411 | |
| 412 | // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns(). |
| 413 | isolatedResolver.createBidiRunsForLine(endOfRuns, NoVisualOverride); |
| 414 | // Note that we do not delete the runs from the resolver. |
| 415 | // We're not guaranteed to get any BidiRuns in the previous step. If we don't, we allow the placeholder |
| 416 | // itself to be turned into an InlineBox. We can't remove it here without potentially losing track of |
| 417 | // the logically last run. |
| 418 | if (isolatedResolver.runs().runCount()) |
| 419 | bidiRuns.replaceRunWithRuns(&isolatedRun.runToReplace, isolatedResolver.runs()); |
| 420 | |
| 421 | // If we encountered any nested isolate runs, just move them |
| 422 | // to the top resolver's list for later processing. |
| 423 | while (!isolatedResolver.isolatedRuns().isEmpty()) { |
| 424 | auto runWithContext = WTF::move(isolatedResolver.isolatedRuns().last()); |
| 425 | isolatedResolver.isolatedRuns().removeLast(); |
| 426 | topResolver.setWhitespaceCollapsingTransitionForIsolatedRun(runWithContext.runToReplace, isolatedResolver.whitespaceCollapsingTransitionForIsolatedRun(runWithContext.runToReplace)); |
| 427 | topResolver.isolatedRuns().append(WTF::move(runWithContext)); |
| 428 | } |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | // This function constructs line boxes for all of the text runs in the resolver and computes their position. |
| 433 | LegacyRootInlineBox* LegacyLineLayout::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const LegacyInlineIterator& end, LineInfo& lineInfo) |
| 434 | { |
| 435 | if (!bidiRuns.runCount()) |
| 436 | return nullptr; |
| 437 | |
| 438 | // FIXME: Why is this only done when we had runs? |
| 439 | lineInfo.setLastLine(!end.renderer()); |
| 440 | |
| 441 | LegacyRootInlineBox* lineBox = constructLine(bidiRuns, lineInfo); |
| 442 | if (!lineBox) |
| 443 | return nullptr; |
| 444 | |
| 445 | lineBox->setBidiLevel(bidiLevel); |
| 446 | |
| 447 | removeEmptyTextBoxesAndUpdateVisualReordering(lineBox, bidiRuns.firstRun()); |
| 448 | |
| 449 | GlyphOverflowAndFallbackFontsMap textBoxDataMap; |
| 450 | lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap); |
| 451 | |
| 452 | return lineBox; |
| 453 | } |
| 454 | |
| 455 | static void repaintSelfPaintInlineBoxes(const LegacyRootInlineBox& rootInlineBox) |
| 456 | { |
| 457 | if (rootInlineBox.hasSelfPaintInlineBox()) { |
| 458 | for (auto* inlineBox = rootInlineBox.firstChild(); inlineBox; inlineBox = inlineBox->nextOnLine()) { |
| 459 | if (auto* renderer = dynamicDowncast<RenderLayerModelObject>(inlineBox->renderer()); renderer && renderer->hasSelfPaintingLayer()) |
| 460 | renderer->repaint(); |
| 461 | } |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | void LegacyLineLayout::layoutRunsAndFloats(bool hasInlineChild) |
| 466 | { |
| 467 | deleteLegacyRootBox(true); |
| 468 | |
| 469 | TextDirection direction = style().writingMode().bidiDirection(); |
| 470 | if (style().unicodeBidi() == UnicodeBidi::Plaintext) |
| 471 | determineDirectionality(direction, LegacyInlineIterator(&m_flow, firstInlineRendererSkippingEmpty(m_flow), 0)); |
| 472 | |
| 473 | InlineBidiResolver resolver; |
| 474 | resolver.setStatus(BidiStatus(direction, isOverride(style().unicodeBidi()))); |
| 475 | LegacyInlineIterator iter = LegacyInlineIterator(&m_flow, firstInlineRendererSkippingEmpty(m_flow, &resolver), 0); |
| 476 | resolver.setPosition(iter, numberOfIsolateAncestors(iter)); |
| 477 | |
| 478 | if (hasInlineChild && !m_flow.selfNeedsLayout()) { |
| 479 | m_flow.setNeedsLayout(MarkOnlyThis); // Mark as needing a full layout to force us to repaint. |
| 480 | if (!layoutContext().needsFullRepaint() && m_flow.cachedLayerClippedOverflowRect()) { |
| 481 | // Because we waited until we were already inside layout to discover |
| 482 | // that the block really needed a full layout, we missed our chance to repaint the layer |
| 483 | // before layout started. Luckily the layer has cached the repaint rect for its original |
| 484 | // position and size, and so we can use that to make a repaint happen now. |
| 485 | m_flow.repaintUsingContainer(m_flow.containerForRepaint().renderer.get(), *m_flow.cachedLayerClippedOverflowRect()); |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | layoutRunsAndFloatsInRange(resolver); |
| 490 | if (legacyRootBox()) |
| 491 | repaintSelfPaintInlineBoxes(*legacyRootBox()); |
| 492 | } |
| 493 | |
| 494 | void LegacyLineLayout::layoutRunsAndFloatsInRange(InlineBidiResolver& resolver) |
| 495 | { |
| 496 | const RenderStyle& styleToUse = style(); |
| 497 | LineWhitespaceCollapsingState& lineWhitespaceCollapsingState = resolver.whitespaceCollapsingState(); |
| 498 | LegacyInlineIterator end = resolver.position(); |
| 499 | RenderTextInfo renderTextInfo; |
| 500 | |
| 501 | LineInfo lineInfo { }; |
| 502 | LineBreaker lineBreaker(m_flow); |
| 503 | |
| 504 | while (!end.atEnd()) { |
| 505 | lineWhitespaceCollapsingState.reset(); |
| 506 | |
| 507 | lineInfo.setEmpty(true); |
| 508 | lineInfo.resetRunsFromLeadingWhitespace(); |
| 509 | |
| 510 | end = lineBreaker.nextLineBreak(resolver, lineInfo, renderTextInfo); |
| 511 | renderTextInfo.lineBreakIteratorFactory.priorContext().reset(); |
| 512 | if (resolver.position().atEnd()) { |
| 513 | // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with! |
| 514 | // Once BidiRunList is separated from BidiResolver this will not be needed. |
| 515 | resolver.runs().clear(); |
| 516 | resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). |
| 517 | resolver.setPosition(LegacyInlineIterator(resolver.position().root(), 0, 0), 0); |
| 518 | break; |
| 519 | } |
| 520 | |
| 521 | ASSERT(end != resolver.position())((void)0); |
| 522 | |
| 523 | if (!lineInfo.isEmpty()) { |
| 524 | VisualDirectionOverride override = (styleToUse.rtlOrdering() == Order::Visual ? (styleToUse.writingMode().isBidiLTR() ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride); |
| 525 | |
| 526 | if (styleToUse.unicodeBidi() == UnicodeBidi::Plaintext && !resolver.context()->parent()) { |
| 527 | TextDirection direction = styleToUse.writingMode().bidiDirection(); |
| 528 | determineDirectionality(direction, resolver.position()); |
| 529 | resolver.setStatus(BidiStatus(direction, isOverride(styleToUse.unicodeBidi()))); |
| 530 | } |
| 531 | // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine. |
| 532 | BidiRunList<BidiRun>& bidiRuns = resolver.runs(); |
| 533 | constructBidiRunsForSegment(resolver, bidiRuns, end, override); |
| 534 | ASSERT(resolver.position() == end)((void)0); |
| 535 | |
| 536 | // Now that the runs have been ordered, we create the line boxes. |
| 537 | |
| 538 | createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, end, lineInfo); |
| 539 | |
| 540 | bidiRuns.clear(); |
| 541 | resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). |
| 542 | } |
| 543 | |
| 544 | if (!lineInfo.isEmpty()) |
| 545 | lineInfo.setFirstLine(false); |
| 546 | |
| 547 | lineWhitespaceCollapsingState.reset(); |
| 548 | resolver.setPosition(end, numberOfIsolateAncestors(end)); |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | void LegacyLineLayout::layoutLineBoxes() |
| 553 | { |
| 554 | m_flow.setLogicalHeight(0_lu); |
| 555 | |
| 556 | deleteLegacyRootBox(); |
| 557 | |
| 558 | if (m_flow.firstChild()) { |
| 559 | // In full layout mode, clear the line boxes of children upfront. Otherwise, |
| 560 | // siblings can run into stale root lineboxes during layout. Then layout |
| 561 | // the replaced elements later. In partial layout mode, line boxes are not |
| 562 | // deleted and only dirtied. In that case, we can layout the replaced |
| 563 | // elements at the same time. |
| 564 | bool hasInlineChild = false; |
| 565 | for (InlineWalker walker(m_flow); !walker.atEnd(); walker.advance()) { |
| 566 | RenderObject& o = *walker.current(); |
| 567 | |
| 568 | if (!hasInlineChild && o.isInline()) |
| 569 | hasInlineChild = true; |
| 570 | |
| 571 | dirtyLineBoxesForRenderer(o); |
| 572 | o.clearNeedsLayout(); |
| 573 | } |
| 574 | |
| 575 | layoutRunsAndFloats(hasInlineChild); |
| 576 | } |
| 577 | |
| 578 | if (!legacyRootBox() && m_flow.hasLineIfEmpty()) |
| 579 | m_flow.setLogicalHeight(m_flow.logicalHeight() + m_flow.lineHeight()); |
| 580 | } |
| 581 | |
| 582 | void LegacyLineLayout::addOverflowFromInlineChildren() |
| 583 | { |
| 584 | if (auto* rootBox = legacyRootBox()) { |
| 585 | LayoutRect childVisualOverflowRect = rootBox->visualOverflowRect(rootBox->lineTop(), rootBox->lineBottom()); |
| 586 | m_flow.addVisualOverflow(childVisualOverflowRect); |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | size_t LegacyLineLayout::lineCount() const |
| 591 | { |
| 592 | return legacyRootBox() ? 1 : 0; |
| 593 | } |
| 594 | |
| 595 | const RenderStyle& LegacyLineLayout::style() const |
| 596 | { |
| 597 | return m_flow.style(); |
| 598 | } |
| 599 | |
| 600 | const LocalFrameViewLayoutContext& LegacyLineLayout::layoutContext() const |
| 601 | { |
| 602 | return m_flow.view().frameView().layoutContext(); |
A function 'layoutContext' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object | |
| 603 | } |
| 604 | |
| 605 | void LegacyLineLayout::shiftLineBy(LayoutUnit shiftX, LayoutUnit shiftY) |
| 606 | { |
| 607 | if (m_legacyRootInlineBox) |
| 608 | m_legacyRootInlineBox->adjustPosition(shiftX, shiftY); |
| 609 | } |
| 610 | |
| 611 | void LegacyLineLayout::deleteLegacyRootBox(bool runCleanup) |
| 612 | { |
| 613 | if (!m_legacyRootInlineBox) |
| 614 | return; |
| 615 | |
| 616 | if (!runCleanup) { |
| 617 | m_legacyRootInlineBox = { }; |
| 618 | return; |
| 619 | } |
| 620 | |
| 621 | auto* rootInlineBoxToDestroy = m_legacyRootInlineBox.release(); |
| 622 | rootInlineBoxToDestroy->deleteLine(); |
| 623 | } |
| 624 | |
| 625 | } |