Title: [285374] trunk/Source/WebCore
Revision
285374
Author
[email protected]
Date
2021-11-06 12:41:20 -0700 (Sat, 06 Nov 2021)

Log Message

[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:

Modified Paths

Diff

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);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to