Modified: trunk/Source/WebCore/ChangeLog (285373 => 285374)
--- trunk/Source/WebCore/ChangeLog 2021-11-06 19:20:59 UTC (rev 285373)
+++ trunk/Source/WebCore/ChangeLog 2021-11-06 19:41:20 UTC (rev 285374)
@@ -1,3 +1,26 @@
+2021-11-06 Alan Bujtas <[email protected]>
+
+ [LFC][IFC] Process all replacement, control and opaque inline items.
+ https://bugs.webkit.org/show_bug.cgi?id=232772
+
+ Reviewed by Antti Koivisto.
+
+ Keep track of each inline item position in the paragraph content (including the opaque items with nullopt position) in a vector.
+ (This offset vector is always in sync with the InlineItems vector (i.e. nth value refers to the nth inline item)
+ Note that it needs to grow together with the InlineItems as we split inline text content on bidi boundaries.)
+
+ 1. Walk the InlineItems up to the bidi end position and assign the computed bidi level to each entry.
+ 2. Split the inline text item on the bidi boundary as needed.
+ 3. Post-process the opaque items (items that are not part of the paragraph content).
+
+ * layout/formattingContexts/inline/InlineItemsBuilder.cpp:
+ (WebCore::Layout::buildBidiParagraph):
+ (WebCore::Layout::InlineItemsBuilder::breakAndComputeBidiLevels):
+ * layout/formattingContexts/inline/InlineTextItem.cpp:
+ (WebCore::Layout::InlineTextItem::split):
+ (WebCore::Layout::InlineTextItem::splitAt): Deleted.
+ * layout/formattingContexts/inline/InlineTextItem.h:
+
2021-11-06 Antti Koivisto <[email protected]>
Remove separate classes for CSS wide keywords (initial/inherit/unset/revert)
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineItemsBuilder.cpp (285373 => 285374)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineItemsBuilder.cpp 2021-11-06 19:20:59 UTC (rev 285373)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineItemsBuilder.cpp 2021-11-06 19:41:20 UTC (rev 285374)
@@ -139,32 +139,37 @@
}
}
-using ContentOffsetMap = HashMap<const Box*, size_t>;
-static inline void buildBidiParagraph(const InlineItems& inlineItems, StringBuilder& paragraphContentBuilder, ContentOffsetMap& contentOffsetMap)
+using InlineItemOffsetList = Vector<std::optional<size_t>>;
+static inline void buildBidiParagraph(const InlineItems& inlineItems, StringBuilder& paragraphContentBuilder, InlineItemOffsetList& inlineItemOffsetList)
{
const Box* lastInlineTextBox = nullptr;
- for (auto& inlineItem : inlineItems) {
+ size_t inlineTextBoxOffset = 0;
+ for (size_t index = 0; index < inlineItems.size(); ++index) {
+ auto& inlineItem = inlineItems[index];
auto& layoutBox = inlineItem.layoutBox();
if (inlineItem.isText()) {
if (lastInlineTextBox != &layoutBox) {
- contentOffsetMap.set(&layoutBox, paragraphContentBuilder.length());
+ inlineTextBoxOffset = paragraphContentBuilder.length();
paragraphContentBuilder.append(downcast<InlineTextBox>(layoutBox).content());
lastInlineTextBox = &layoutBox;
}
- } else if (inlineItem.isBox())
+ inlineItemOffsetList.uncheckedAppend({ inlineTextBoxOffset + downcast<InlineTextItem>(inlineItem).start() });
+ } else if (inlineItem.isBox()) {
+ inlineItemOffsetList.uncheckedAppend({ paragraphContentBuilder.length() });
paragraphContentBuilder.append(objectReplacementCharacter);
+ }
else if (inlineItem.isInlineBoxStart() || inlineItem.isInlineBoxEnd()) {
// https://drafts.csswg.org/css-writing-modes/#unicode-bidi
auto& style = inlineItem.style();
auto initiatesControlCharacter = style.rtlOrdering() == Order::Logical && style.unicodeBidi() != EUnicodeBidi::UBNormal;
- if (!initiatesControlCharacter)
+ if (!initiatesControlCharacter) {
+ // Opaque items do not have position in the bidi paragraph. They inherit their bidi level from the next inline item.
+ inlineItemOffsetList.uncheckedAppend({ });
continue;
+ }
+ inlineItemOffsetList.uncheckedAppend({ paragraphContentBuilder.length() });
auto isEnteringBidi = inlineItem.isInlineBoxStart();
-
- if (isEnteringBidi)
- contentOffsetMap.add(&layoutBox, paragraphContentBuilder.length());
-
switch (style.unicodeBidi()) {
case EUnicodeBidi::UBNormal:
// The box does not open an additional level of embedding with respect to the bidirectional algorithm.
@@ -206,8 +211,10 @@
ASSERT(!inlineItems.isEmpty());
StringBuilder paragraphContentBuilder;
- ContentOffsetMap contentOffsetMap;
- buildBidiParagraph(inlineItems, paragraphContentBuilder, contentOffsetMap);
+ InlineItemOffsetList inlineItemOffsets;
+ inlineItemOffsets.reserveInitialCapacity(inlineItems.size());
+ buildBidiParagraph(inlineItems, paragraphContentBuilder, inlineItemOffsets);
+ ASSERT(inlineItemOffsets.size() == inlineItems.size());
// 1. Setup the bidi boundary loop by calling ubidi_setPara with the paragraph text.
// 2. Call ubidi_getLogicalRun to advance to the next bidi boundary until we hit the end of the content.
@@ -218,61 +225,74 @@
ubidi_close(ubidi);
});
- UBiDiLevel bidiLevel = UBIDI_DEFAULT_LTR;
+ UBiDiLevel rootBidiLevel = UBIDI_DEFAULT_LTR;
bool useHeuristicBaseDirection = root().style().unicodeBidi() == EUnicodeBidi::Plaintext;
if (!useHeuristicBaseDirection)
- bidiLevel = root().style().isLeftToRightDirection() ? UBIDI_LTR : UBIDI_RTL;
+ rootBidiLevel = root().style().isLeftToRightDirection() ? UBIDI_LTR : UBIDI_RTL;
UErrorCode error = U_ZERO_ERROR;
ASSERT(!paragraphContentBuilder.isEmpty());
- ubidi_setPara(ubidi, paragraphContentBuilder.characters16(), paragraphContentBuilder.length(), bidiLevel, nullptr, &error);
+ ubidi_setPara(ubidi, paragraphContentBuilder.characters16(), paragraphContentBuilder.length(), rootBidiLevel, nullptr, &error);
if (U_FAILURE(error)) {
ASSERT_NOT_REACHED();
return;
}
- size_t currentInlineItemIndex = 0;
+ size_t inlineItemIndex = 0;
+ auto hasSeenOpaqueItem = false;
for (size_t currentPosition = 0; currentPosition < paragraphContentBuilder.length();) {
- UBiDiLevel level;
+ UBiDiLevel bidiLevel;
int32_t endPosition = currentPosition;
- ubidi_getLogicalRun(ubidi, currentPosition, &endPosition, &level);
+ ubidi_getLogicalRun(ubidi, currentPosition, &endPosition, &bidiLevel);
- auto setBidiLevelOnRange = [&] {
+ auto setBidiLevelOnRange = [&](size_t bidiEnd, auto bidiLevelForRange) {
// We should always have inline item(s) associated with a bidi range.
- ASSERT(currentInlineItemIndex < inlineItems.size());
-
- while (currentInlineItemIndex < inlineItems.size()) {
- auto& inlineItem = inlineItems[currentInlineItemIndex];
- if (!inlineItem.isText()) {
- // FIXME: This fails with multiple inline boxes as they don't advance position.
- inlineItem.setBidiLevel(level);
- ++currentInlineItemIndex;
+ ASSERT(inlineItemIndex < inlineItemOffsets.size());
+ // Start of the range is always where we left off (bidi ranges do not have gaps).
+ for (; inlineItemIndex < inlineItemOffsets.size(); ++inlineItemIndex) {
+ auto offset = inlineItemOffsets[inlineItemIndex];
+ if (!offset) {
+ // This is an opaque item. Let's post-process it.
+ hasSeenOpaqueItem = true;
continue;
}
- // FIXME: Find out if this is the most optimal place to measure and cache text widths.
- auto& inlineTextItem = downcast<InlineTextItem>(inlineItem);
- inlineTextItem.setBidiLevel(level);
-
- auto inlineTextItemEnd = inlineTextItem.end();
- auto bidiEnd = endPosition - contentOffsetMap.get(&inlineTextItem.layoutBox());
- if (bidiEnd == inlineTextItemEnd) {
- ++currentInlineItemIndex;
+ if (*offset >= bidiEnd) {
+ // This inline item is outside of the bidi range.
break;
}
- if (bidiEnd < inlineTextItemEnd) {
- if (currentInlineItemIndex == inlineItems.size() - 1)
- inlineItems.append(inlineTextItem.splitAt(bidiEnd));
- else
- inlineItems.insert(currentInlineItemIndex + 1, inlineTextItem.splitAt(bidiEnd));
- ++currentInlineItemIndex;
+ auto& inlineItem = inlineItems[inlineItemIndex];
+ inlineItem.setBidiLevel(bidiLevelForRange);
+ if (!inlineItem.isText())
+ continue;
+ // Check if this text item is on bidi boundary and needs splitting.
+ auto& inlineTextItem = downcast<InlineTextItem>(inlineItem);
+ auto endPosition = *offset + inlineTextItem.length();
+ if (endPosition > bidiEnd) {
+ inlineItems.insert(inlineItemIndex + 1, inlineTextItem.split(bidiEnd - *offset));
+ // Right side is going to be processed at the next bidi range.
+ inlineItemOffsets.insert(inlineItemIndex + 1, bidiEnd);
+ ++inlineItemIndex;
break;
}
- ++currentInlineItemIndex;
}
};
- setBidiLevelOnRange();
+ setBidiLevelOnRange(endPosition, bidiLevel);
currentPosition = endPosition;
}
+
+ auto setBidiLevelForOpaqueInlineItems = [&] {
+ if (!hasSeenOpaqueItem)
+ return;
+ // Opaque items (inline items with no paragraph content) get their bidi level values from their adjacent items.
+ auto lastBidiLevel = rootBidiLevel;
+ for (auto index = inlineItems.size(); index--;) {
+ if (!inlineItemOffsets[index])
+ inlineItems[index].setBidiLevel(lastBidiLevel);
+ else
+ lastBidiLevel = inlineItems[index].bidiLevel();
+ }
+ };
+ setBidiLevelForOpaqueInlineItems();
}
void InlineItemsBuilder::handleTextContent(const InlineTextBox& inlineTextBox, InlineItems& inlineItems)
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.cpp (285373 => 285374)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.cpp 2021-11-06 19:20:59 UTC (rev 285373)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.cpp 2021-11-06 19:41:20 UTC (rev 285374)
@@ -71,11 +71,11 @@
return { inlineTextBox(), end() - length, length, bidiLevel(), hasTrailingSoftHyphen(), isWordSeparator(), width, m_textItemType };
}
-InlineTextItem InlineTextItem::splitAt(size_t position)
+InlineTextItem InlineTextItem::split(size_t leftSideLength)
{
RELEASE_ASSERT(length() > 1);
- RELEASE_ASSERT(position > start() && position < end());
- auto rightSide = right(end() - position, { });
+ RELEASE_ASSERT(leftSideLength && leftSideLength < length());
+ auto rightSide = right(length() - leftSideLength, { });
m_length = length() - rightSide.length();
return rightSide;
}
Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.h (285373 => 285374)
--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.h 2021-11-06 19:20:59 UTC (rev 285373)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineTextItem.h 2021-11-06 19:41:20 UTC (rev 285374)
@@ -64,7 +64,7 @@
friend class InlineItemsBuilder;
using InlineItem::TextItemType;
- InlineTextItem splitAt(size_t position);
+ InlineTextItem split(size_t leftSideLength);
InlineTextItem(const InlineTextBox&, unsigned start, unsigned length, UBiDiLevel, bool hasTrailingSoftHyphen, bool isWordSeparator, std::optional<InlineLayoutUnit> width, TextItemType);
explicit InlineTextItem(const InlineTextBox&, UBiDiLevel);