Changeset 257291 in webkit
- Timestamp:
- Feb 24, 2020 5:42:47 PM (4 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r257290 r257291 1 2020-02-24 Zalan Bujtas <zalan@apple.com> 2 3 [LFC][IFC][Floats] Fix float boxes embedded to unbreakable inline runs. 4 https://bugs.webkit.org/show_bug.cgi?id=208112 5 <rdar://problem/59709701> 6 7 Reviewed by Antti Koivisto. 8 9 This patch fixes the cases when the float is embedded to otherwise unbreakable inline content. 10 e.g. "text_<div style="float: left"></div>_content" 11 12 The logic goes like this: 13 1. collect the floats inside the unbreakable candidate content 14 2. mark them intrusive if they potentially influence the current line 15 3. at handleFloatsAndInlineContent(), adjust available width with the intrusive floats first 16 4. feed the inline content to the LineBreaker 17 6. commit the float content based on the line breaking result (commit none, partially, all). 18 (Note that this algorithm produces a different layout compared to WebKit trunk. It mostly matches FireFox though.) 19 20 * layout/inlineformatting/InlineFormattingContext.cpp: 21 (WebCore::Layout::InlineFormattingContext::computedIntrinsicWidthForConstraint const): 22 (WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine): 23 * layout/inlineformatting/InlineLineBuilder.cpp: 24 (WebCore::Layout::LineBuilder::moveLogicalLeft): 25 (WebCore::Layout::LineBuilder::moveLogicalRight): 26 * layout/inlineformatting/LineLayoutContext.cpp: 27 (WebCore::Layout::isAtSoftWrapOpportunity): 28 (WebCore::Layout::nextWrapOpportunity): 29 (WebCore::Layout::LineCandidate::FloatContent::append): 30 (WebCore::Layout::LineLayoutContext::layoutLine): 31 (WebCore::Layout::LineLayoutContext::close): 32 (WebCore::Layout::LineLayoutContext::nextContentForLine): 33 (WebCore::Layout::LineLayoutContext::addIntrusiveFloats): 34 (WebCore::Layout::LineLayoutContext::revertIntrusiveFloats): 35 (WebCore::Layout::LineLayoutContext::handleFloatsAndInlineContent): 36 (WebCore::Layout::isLineConsideredEmpty): Deleted. 37 (WebCore::Layout::LineLayoutContext::tryAddingFloatContent): Deleted. 38 (WebCore::Layout::LineLayoutContext::tryAddingInlineItems): Deleted. 39 * layout/inlineformatting/LineLayoutContext.h: 40 1 41 2020-02-24 Nikos Mouchtaris <nmouchtaris@apple.com> 2 42 -
trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
r256928 r257291 241 241 // Only the horiztonal available width is constrained when computing intrinsic width. 242 242 lineBuilder.initialize(LineBuilder::Constraints { { }, horizontalConstraints.logicalWidth, false, { } }); 243 auto lineContent = lineLayoutContext.layoutLine(lineBuilder, layoutRange , { }); 244 243 auto lineContent = lineLayoutContext.layoutLine(lineBuilder, layoutRange, { }); 245 244 layoutRange.start = *lineContent.trailingInlineItemIndex + 1; 246 InlineLayoutUnit floatsWidth = 0; 247 for (auto& floatItem : lineContent.floats) 248 floatsWidth += geometryForBox(floatItem->layoutBox()).marginBoxWidth(); 249 maximumLineWidth = std::max(maximumLineWidth, floatsWidth + lineContent.lineBox.logicalWidth()); 245 // FIXME: Use line logical left and right to take floats into account. 246 maximumLineWidth = std::max(maximumLineWidth, lineContent.lineBox.logicalWidth()); 250 247 } 251 248 return maximumLineWidth; … … 438 435 auto floatingContext = FloatingContext { root(), *this, formattingState.floatingState() }; 439 436 // Move floats to their final position. 440 for (const auto& float Item: lineContent.floats) {441 auto& floatBox = float Item->layoutBox();437 for (const auto& floatCandidate : lineContent.floats) { 438 auto& floatBox = floatCandidate.item->layoutBox(); 442 439 auto& displayBox = formattingState.displayBox(floatBox); 443 440 // Set static position first. 444 displayBox.setTopLeft({ lineBox.logicalLeft(), lineBox.logicalTop() }); 441 auto verticalStaticPosition = floatCandidate.isIntrusive == LineLayoutContext::LineContent::Float::Intrusive::Yes ? lineBox.logicalTop() : lineBox.logicalBottom(); 442 displayBox.setTopLeft({ lineBox.logicalLeft(), verticalStaticPosition }); 445 443 // Float it. 446 444 displayBox.setTopLeft(floatingContext.positionForFloat(floatBox, horizontalConstraints)); -
trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.cpp
r257236 r257291 73 73 // An incoming <img> box would enable us to commit the "<span>prior_continuous_content</span>" content 74 74 // but an incoming text content would not necessarily. 75 ASSERT(current.isText() || current.isBox() || current.isFloat());76 ASSERT(next.isText() || next.isBox() || next.isFloat());75 ASSERT(current.isText() || current.isBox()); 76 ASSERT(next.isText() || next.isBox()); 77 77 if (current.isText() && next.isText()) { 78 78 auto& currentInlineTextItem = downcast<InlineTextItem>(current); … … 102 102 return true; 103 103 } 104 if (current.isFloat() || next.isFloat()) {105 // Floats are not part of the inline content. We should treat them as if they were not here as far as wrap opportunitues.106 // [text][float box][text] is essentially just [text][text]107 return false;108 }109 104 ASSERT_NOT_REACHED(); 110 105 return true; … … 124 119 125 120 auto inlineItemIndexWithContent = [&] (auto index) { 126 // Break at the first text/box/line break inline item. 121 // Note that floats are not part of the inline content. We should treat them as if they were not here as far as wrap opportunities are concerned. 122 // [text][float box][text] is essentially just [text][text] 127 123 for (; index < layoutRange.end; ++index) { 128 124 auto& inlineItem = inlineContent[index]; 129 if (inlineItem.isText() || inlineItem.isBox() || inlineItem.isFloat())125 if (inlineItem.isText() || inlineItem.isBox()) 130 126 return index; 131 127 if (inlineItem.isLineBreak()) { … … 198 194 199 195 struct FloatContent { 200 void append(const InlineItem& floatItem) { m_floatList.append(&floatItem); } 201 202 using FloatList = Vector<const InlineItem*>; 196 void append(const InlineItem& floatItem, InlineLayoutUnit logicalWidth, bool isIntrusive); 197 198 struct Float { 199 const InlineItem* item { nullptr }; 200 InlineLayoutUnit logicalWidth { 0 }; 201 bool isIntrusive { true }; 202 }; 203 using FloatList = Vector<Float>; 203 204 const FloatList& list() const { return m_floatList; } 204 205 void reset() { m_floatList.clear(); } 205 InlineLayoutUnit intrusiveWidth() const { return m_intrusiveWidth; } 206 207 void reset(); 206 208 207 209 private: 208 210 FloatList m_floatList; 211 InlineLayoutUnit m_intrusiveWidth { 0 }; 209 212 }; 210 213 // Candidate content is a collection of inline items and/or float boxes. … … 219 222 } 220 223 224 inline void LineCandidate::InlineContent::reset() 225 { 226 m_LogicalWidth = { }; 227 m_inlineRuns.clear(); 228 m_trailingLineBreak = { }; 229 } 230 231 inline void LineCandidate::FloatContent::append(const InlineItem& floatItem, InlineLayoutUnit logicalWidth, bool isIntrusive) 232 { 233 if (isIntrusive) 234 m_intrusiveWidth += logicalWidth; 235 m_floatList.append({ &floatItem, logicalWidth, isIntrusive }); 236 } 237 238 inline void LineCandidate::FloatContent::reset() 239 { 240 m_floatList.clear(); 241 m_intrusiveWidth = { }; 242 } 243 221 244 inline void LineCandidate::reset() 222 245 { 223 246 floatContent.reset(); 224 247 inlineContent.reset(); 225 }226 227 inline void LineCandidate::InlineContent::reset()228 {229 m_LogicalWidth = 0;230 m_inlineRuns.clear();231 m_trailingLineBreak = nullptr;232 248 } 233 249 … … 262 278 // Non-replaced inline box (e.g. inline-block) 263 279 return boxGeometry.width(); 264 }265 266 static inline bool isLineConsideredEmpty(const LineBuilder& line)267 {268 return line.isVisuallyEmpty() && !line.hasIntrusiveFloat();269 280 } 270 281 … … 290 301 // 3. Check if the content fits the line and commit the content accordingly (full, partial or not commit at all). 291 302 // 4. Return if we are at the end of the line either by not being able to fit more content or because of an explicit line break. 292 nextContentForLine(lineCandidate, currentItemIndex, layoutRange, partialLeadingContentLength, line.lineBox().logicalWidth()); 293 if (lineCandidate.floatContent.list().isEmpty()) { 294 // Floats shrink the available horizontal space for the rest of the content, but they are not added on the line. 295 auto result = tryAddingFloatContent(line, lineCandidate); 296 committedInlineItemCount += result.committedCount.value; 297 if (result.isEndOfLine == LineBreaker::IsEndOfLine::Yes) { 298 // This float takes up all the horizontal space. 299 return close(line, layoutRange, committedInlineItemCount, { }); 300 } 301 } 303 nextContentForLine(lineCandidate, currentItemIndex, layoutRange, partialLeadingContentLength, line.availableWidth() + line.trimmableTrailingWidth(), line.lineBox().logicalWidth()); 304 // Now check if we can put this content on the current line. 305 auto result = handleFloatsAndInlineContent(lineBreaker, line, layoutRange, lineCandidate); 306 committedInlineItemCount = result.committedCount.isRevert ? result.committedCount.value : committedInlineItemCount + result.committedCount.value; 302 307 auto& inlineContent = lineCandidate.inlineContent; 303 // Now check if we can put this content on the current line.304 auto result = tryAddingInlineItems(lineBreaker, line, layoutRange, lineCandidate);305 committedInlineItemCount = result.committedCount.isRevert ? result.committedCount.value : committedInlineItemCount + result.committedCount.value;306 308 auto inlineContentIsFullyCommitted = inlineContent.runs().size() == result.committedCount.value && !result.partialContent; 307 309 auto isEndOfLine = result.isEndOfLine == LineBreaker::IsEndOfLine::Yes; … … 317 319 return close(line, layoutRange, committedInlineItemCount, result.partialContent); 318 320 } 319 currentItemIndex = layoutRange.start + committedInlineItemCount ;321 currentItemIndex = layoutRange.start + committedInlineItemCount + m_floats.size(); 320 322 partialLeadingContentLength = { }; 321 323 } … … 327 329 { 328 330 ASSERT(committedInlineItemCount || line.hasIntrusiveFloat()); 329 if (!committedInlineItemCount) 330 return LineContent { { }, { }, WTFMove(m_floats), line.close(), line.lineBox() }; 331 331 if (!committedInlineItemCount) { 332 if (m_floats.isEmpty()) { 333 // We didn't manage to add a run or a float at this vertical position. 334 return LineContent { { }, { }, WTFMove(m_floats), line.close(), line.lineBox() }; 335 } 336 auto trailingInlineItemIndex = layoutRange.start + m_floats.size(); 337 return LineContent { trailingInlineItemIndex, { }, WTFMove(m_floats), line.close(), line.lineBox() }; 338 } 332 339 // Adjust hyphenated line count. 333 340 if (partialContent && partialContent->trailingContentHasHyphen) … … 335 342 else 336 343 m_successiveHyphenatedLineCount = 0; 337 unsigned trailingInlineItemIndex = layoutRange.start + committedInlineItemCount - 1; 344 ASSERT(committedInlineItemCount); 345 auto trailingInlineItemIndex = layoutRange.start + committedInlineItemCount + m_floats.size() - 1; 338 346 ASSERT(trailingInlineItemIndex < layoutRange.end); 339 347 auto isLastLineWithInlineContent = [&] { … … 354 362 } 355 363 356 void LineLayoutContext::nextContentForLine(LineCandidate& lineCandidate, unsigned currentInlineItemIndex, const InlineItemRange layoutRange, Optional<unsigned> partialLeadingContentLength, InlineLayoutUnit currentLogicalRight)364 void LineLayoutContext::nextContentForLine(LineCandidate& lineCandidate, unsigned currentInlineItemIndex, const InlineItemRange layoutRange, Optional<unsigned> partialLeadingContentLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight) 357 365 { 358 366 ASSERT(currentInlineItemIndex < layoutRange.end); … … 375 383 } 376 384 385 auto accumulatedWidth = InlineLayoutUnit { }; 377 386 for (auto index = currentInlineItemIndex; index < softWrapOpportunityIndex; ++index) { 378 387 auto& inlineItem = m_inlineItems[index]; 379 388 if (inlineItem.isFloat()) { 380 389 // Floats are not part of the line context. 381 // FIXME: Check if their width should be added to currentLogicalRight. 382 lineCandidate.floatContent.append(inlineItem); 390 auto floatWidth = inlineItemWidth(inlineItem, { }); 391 lineCandidate.floatContent.append(inlineItem, floatWidth, floatWidth <= (availableLineWidth - accumulatedWidth)); 392 accumulatedWidth += floatWidth; 383 393 continue; 384 394 } … … 387 397 lineCandidate.inlineContent.appendInlineItem(inlineItem, inlineItenmWidth); 388 398 currentLogicalRight += inlineItenmWidth; 399 accumulatedWidth += inlineItenmWidth; 389 400 continue; 390 401 } … … 397 408 } 398 409 399 LineLayoutContext::Result LineLayoutContext::tryAddingFloatContent(LineBuilder& line, const LineCandidate& lineCandidate)410 void LineLayoutContext::commitFloats(LineBuilder& line, const LineCandidate& lineCandidate, CommitIntrusiveFloatsOnly commitIntrusiveOnly) 400 411 { 401 412 auto& floatContent = lineCandidate.floatContent; 402 auto availableLineWidth = line.availableWidth() + line.trimmableTrailingWidth(); 403 auto accumulatedFloatsWidth = InlineLayoutUnit { }; 404 size_t committedCount = 0; 405 406 for (auto* floatCandidate : floatContent.list()) { 407 auto logicalWidth = inlineItemWidth(*floatCandidate, { }); 408 auto availableWidthForFloat = availableLineWidth - accumulatedFloatsWidth; 409 accumulatedFloatsWidth += logicalWidth; 410 411 if (availableWidthForFloat < logicalWidth) 412 return { LineBreaker::IsEndOfLine::Yes, { committedCount, false } }; 413 414 // This float can sit on the current line. 415 auto& floatBox = floatCandidate->layoutBox(); 416 // Shrink available space for current line and move existing inline runs. 413 auto leftFloatsWidth = InlineLayoutUnit { }; 414 auto rightFloatsWidth = InlineLayoutUnit { }; 415 416 for (auto& floatCandidate : floatContent.list()) { 417 if (floatCandidate.isIntrusive && commitIntrusiveOnly == CommitIntrusiveFloatsOnly::Yes) 418 continue; 419 if (!floatCandidate.isIntrusive) { 420 m_floats.append({ LineContent::Float::Intrusive::No, floatCandidate.item }); 421 continue; 422 } 423 m_floats.append({ LineContent::Float::Intrusive::Yes, floatCandidate.item }); 424 // This float is intrusive and it shrinks the current line. 425 // Shrink available space for current line. 426 if (floatCandidate.item->layoutBox().isLeftFloatingPositioned()) 427 leftFloatsWidth += floatCandidate.logicalWidth; 428 else 429 rightFloatsWidth += floatCandidate.logicalWidth; 430 } 431 if (leftFloatsWidth || rightFloatsWidth) { 417 432 line.setHasIntrusiveFloat(); 418 if (floatBox.isLeftFloatingPositioned()) 419 line.moveLogicalLeft(logicalWidth); 420 else 421 line.moveLogicalRight(logicalWidth); 422 m_floats.append(floatCandidate); 423 ++committedCount; 424 } 425 return { LineBreaker::IsEndOfLine::No, { committedCount, false } }; 426 } 427 428 LineLayoutContext::Result LineLayoutContext::tryAddingInlineItems(LineBreaker& lineBreaker, LineBuilder& line, const InlineItemRange& layoutRange, const LineCandidate& lineCandidate) 433 if (leftFloatsWidth) 434 line.moveLogicalLeft(leftFloatsWidth); 435 if (rightFloatsWidth) 436 line.moveLogicalRight(rightFloatsWidth); 437 } 438 } 439 440 LineLayoutContext::Result LineLayoutContext::handleFloatsAndInlineContent(LineBreaker& lineBreaker, LineBuilder& line, const InlineItemRange& layoutRange, const LineCandidate& lineCandidate) 429 441 { 430 442 auto& inlineContent = lineCandidate.inlineContent; 431 443 auto& candidateRuns = inlineContent.runs(); 432 433 if (candidateRuns.isEmpty())444 if (candidateRuns.isEmpty()) { 445 commitFloats(line, lineCandidate); 434 446 return { LineBreaker::IsEndOfLine::No }; 447 } 435 448 436 449 auto shouldDisableHyphenation = [&] { … … 442 455 lineBreaker.setHyphenationDisabled(); 443 456 457 auto& floatContent = lineCandidate.floatContent; 444 458 // Check if this new content fits. 445 auto lineStatus = LineBreaker::LineStatus { line.availableWidth(), line.trimmableTrailingWidth(), line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty(line) }; 459 auto availableWidth = line.availableWidth() - floatContent.intrusiveWidth(); 460 auto isLineConsideredEmpty = line.isVisuallyEmpty() && !line.hasIntrusiveFloat(); 461 auto lineStatus = LineBreaker::LineStatus { availableWidth, line.trimmableTrailingWidth(), line.isTrailingRunFullyTrimmable(), isLineConsideredEmpty }; 446 462 auto result = lineBreaker.shouldWrapInlineContent(candidateRuns, inlineContent.logicalWidth(), lineStatus); 447 463 if (result.lastWrapOpportunityItem) 448 464 m_lastWrapOpportunityItem = result.lastWrapOpportunityItem; 449 465 if (result.action == LineBreaker::Result::Action::Keep) { 450 // This continuous content can be fully placed on the current line .466 // This continuous content can be fully placed on the current line including non-intrusive floats. 451 467 for (auto& run : candidateRuns) 452 468 line.append(run.inlineItem, run.logicalWidth); 469 commitFloats(line, lineCandidate); 453 470 return { result.isEndOfLine, { candidateRuns.size(), false } }; 454 471 } … … 467 484 ASSERT(result.isEndOfLine == LineBreaker::IsEndOfLine::Yes); 468 485 // Commit the combination of full and partial content on the current line. 486 commitFloats(line, lineCandidate, CommitIntrusiveFloatsOnly::Yes); 469 487 ASSERT(result.partialTrailingContent); 470 488 commitPartialContent(line, candidateRuns, *result.partialTrailingContent); -
trunk/Source/WebCore/layout/inlineformatting/LineLayoutContext.h
r257236 r257291 47 47 Optional<unsigned> trailingInlineItemIndex; 48 48 Optional<PartialContent> partialContent; 49 Vector<const InlineItem*> floats; 49 struct Float { 50 enum class Intrusive { No, Yes }; 51 Intrusive isIntrusive { Intrusive::Yes }; 52 const InlineItem* item { nullptr }; 53 }; 54 using FloatList = Vector<Float>; 55 FloatList floats; 50 56 const LineBuilder::RunList runList; 51 57 const LineBoxBuilder lineBox; … … 60 66 61 67 private: 62 void nextContentForLine(LineCandidate&, unsigned inlineItemIndex, const InlineItemRange layoutRange, Optional<unsigned> overflowLength, InlineLayoutUnit currentLogicalRight);68 void nextContentForLine(LineCandidate&, unsigned inlineItemIndex, const InlineItemRange layoutRange, Optional<unsigned> overflowLength, InlineLayoutUnit availableLineWidth, InlineLayoutUnit currentLogicalRight); 63 69 struct Result { 64 70 LineBreaker::IsEndOfLine isEndOfLine { LineBreaker::IsEndOfLine::No }; … … 70 76 Optional <LineContent::PartialContent> partialContent { }; 71 77 }; 72 Result tryAddingFloatContent(LineBuilder&, const LineCandidate&); 73 Result tryAddingInlineItems(LineBreaker&, LineBuilder&, const InlineItemRange& layoutRange, const LineCandidate&); 78 enum class CommitIntrusiveFloatsOnly { No, Yes }; 79 void commitFloats(LineBuilder&, const LineCandidate&, CommitIntrusiveFloatsOnly = CommitIntrusiveFloatsOnly::No); 80 Result handleFloatsAndInlineContent(LineBreaker&, LineBuilder&, const InlineItemRange& layoutRange, const LineCandidate&); 74 81 size_t rebuildLine(LineBuilder&, const InlineItemRange& layoutRange); 75 82 void commitPartialContent(LineBuilder&, const LineBreaker::RunList&, const LineBreaker::Result::PartialTrailingContent&); … … 84 91 const ContainerBox& m_formattingContextRoot; 85 92 const InlineItems& m_inlineItems; 86 using FloatList = Vector<const InlineItem*>; 87 FloatList m_floats; 93 LineContent::FloatList m_floats; 88 94 Optional<InlineTextItem> m_partialLeadingTextItem; 89 95 const InlineItem* m_lastWrapOpportunityItem { nullptr };
Note: See TracChangeset
for help on using the changeset viewer.