Diff
Modified: trunk/LayoutTests/ChangeLog (276387 => 276388)
--- trunk/LayoutTests/ChangeLog 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/LayoutTests/ChangeLog 2021-04-21 20:11:31 UTC (rev 276388)
@@ -1,3 +1,16 @@
+2021-04-21 Wenson Hsieh <[email protected]>
+
+ [iOS] Text selection in image overlays should not be limited to rectilinear quads
+ https://bugs.webkit.org/show_bug.cgi?id=224837
+ <rdar://76829981>
+
+ Reviewed by Tim Horton.
+
+ Add a new layout test to exercise the new selection rendering behavior.
+
+ * fast/images/image-extraction/ios/selection-rects-in-image-overlay-expected.txt: Added.
+ * fast/images/image-extraction/ios/selection-rects-in-image-overlay.html: Added.
+
2021-04-21 Tyler Wilcock <[email protected]>
[css-counter-styles] Parse @counter-style descriptors
Added: trunk/LayoutTests/fast/images/image-extraction/ios/selection-rects-in-image-overlay-expected.txt (0 => 276388)
--- trunk/LayoutTests/fast/images/image-extraction/ios/selection-rects-in-image-overlay-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/images/image-extraction/ios/selection-rects-in-image-overlay-expected.txt 2021-04-21 20:11:31 UTC (rev 276388)
@@ -0,0 +1,6 @@
+PASS initialSelectionRects.length is 1
+PASS selectionRectsAfterSelectingAll.length is 4
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/images/image-extraction/ios/selection-rects-in-image-overlay.html (0 => 276388)
--- trunk/LayoutTests/fast/images/image-extraction/ios/selection-rects-in-image-overlay.html (rev 0)
+++ trunk/LayoutTests/fast/images/image-extraction/ios/selection-rects-in-image-overlay.html 2021-04-21 20:11:31 UTC (rev 276388)
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+<style>
+img {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+</style>
+</head>
+<body>
+<img src=""
+<script>
+jsTestIsAsync = true;
+
+addEventListener("load", async () => {
+ let image = document.querySelector("img");
+ internals.installImageOverlay(image, [
+ {
+ text: "one",
+ topLeft: new DOMPointReadOnly(0.1, 0.25),
+ topRight: new DOMPointReadOnly(0.25, 0.1),
+ bottomRight: new DOMPointReadOnly(0.35, 0.2),
+ bottomLeft: new DOMPointReadOnly(0.2, 0.35)
+ },
+ {
+ text: "two",
+ topLeft: new DOMPointReadOnly(0.3, 0.45),
+ topRight: new DOMPointReadOnly(0.45, 0.3),
+ bottomRight: new DOMPointReadOnly(0.55, 0.4),
+ bottomLeft: new DOMPointReadOnly(0.4, 0.55)
+ },
+ {
+ text: "three",
+ topLeft: new DOMPointReadOnly(0.5, 0.65),
+ topRight: new DOMPointReadOnly(0.65, 0.5),
+ bottomRight: new DOMPointReadOnly(0.75, 0.6),
+ bottomLeft: new DOMPointReadOnly(0.6, 0.75)
+ },
+ {
+ text: "four",
+ topLeft: new DOMPointReadOnly(0.7, 0.85),
+ topRight: new DOMPointReadOnly(0.85, 0.7),
+ bottomRight: new DOMPointReadOnly(0.95, 0.8),
+ bottomLeft: new DOMPointReadOnly(0.8, 0.95)
+ }
+ ]);
+
+ await UIHelper.longPressAtPoint(90, 90);
+ initialSelectionRects = await UIHelper.waitForSelectionToAppear();
+ shouldBe("initialSelectionRects.length", "1");
+
+ testRunner.execCommand("SelectAll");
+ await UIHelper.ensurePresentationUpdate();
+ selectionRectsAfterSelectingAll = await UIHelper.getUISelectionViewRects();
+ shouldBe("selectionRectsAfterSelectingAll.length", "4");
+
+ finishJSTest();
+});
+</script>
+</body>
+</html>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (276387 => 276388)
--- trunk/Source/WebCore/ChangeLog 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/ChangeLog 2021-04-21 20:11:31 UTC (rev 276388)
@@ -1,3 +1,87 @@
+2021-04-21 Wenson Hsieh <[email protected]>
+
+ [iOS] Text selection in image overlays should not be limited to rectilinear quads
+ https://bugs.webkit.org/show_bug.cgi?id=224837
+ <rdar://76829981>
+
+ Reviewed by Tim Horton.
+
+ Refactor the iOS-specific `WebCore::SelectionGeometry` such that it is backed by a `FloatQuad` instead of an
+ `IntRect`, and additionally support a flag to indicate that the selection geometry should render as individual
+ quads, instead of allowing adjacent rects to be coalesced based on each rects' enclosing bounds.
+
+ See comments below for more information.
+
+ Test: fast/images/image-extraction/ios/selection-rects-in-image-overlay.html
+
+ * html/HTMLElement.cpp:
+ (WebCore::HTMLElement::selectionRenderingBehavior):
+ * html/HTMLElement.h:
+ * platform/ios/SelectionGeometry.cpp:
+ (WebCore::SelectionGeometry::SelectionGeometry):
+
+ Change these constructors to take `FloatQuad` instead of an enclosing bounding box of a quad. Refer to call
+ sites below.
+
+ (WebCore::SelectionGeometry::setLogicalLeft):
+ (WebCore::SelectionGeometry::setLogicalWidth):
+ (WebCore::SelectionGeometry::setLogicalTop):
+ (WebCore::SelectionGeometry::setLogicalHeight):
+
+ Adjust these four setters so that they automatically inflate the selection quad to the quad's enclosing bounds
+ before changing any of the dimensions of the rect. Note that in practice, these methods are only used by code
+ that attempts to coalesce adjacent selection geometries, in which case we shouldn't be rendering non-rectilinear
+ quads anyways.
+
+ (WebCore::SelectionGeometry::rect const):
+
+ This method now computes the enclosing bounding rect of the selection quad. Since this can be called many times
+ for a single selection geometry, we cache the enclosing bounds in `m_cachedEnclosingRect` to avoid repeated
+ bounding box computation.
+
+ (WebCore::SelectionGeometry::setQuad):
+
+ Set the selection quad, and invalidate the enclosing bounding rect.
+
+ (WebCore::SelectionGeometry::setRect):
+
+ Since we're already setting the quad from an `IntRect`, we can go ahead and set the cached enclosing rect at the
+ same time to avoid computing it again in the future.
+
+ (WebCore::operator<<):
+ * platform/ios/SelectionGeometry.h:
+
+ Add a few new members to `SelectionGeometry`. Instead of maintaining an `IntRect`, store a `FloatRect` in
+ `m_quad`. Additionally, add a new enum describing how the `SelectionGeometry` should behave. This enum only has
+ two values: `CoalesceBoundingRects`, indicating that adjacent selection geometries should be coalesced based on
+ the enclosing bounding rects of their quads, and `UseIndividualQuads`, indicating that each quad should be
+ rendered individually.
+
+ (WebCore::SelectionGeometry::quad const):
+ (WebCore::SelectionGeometry::logicalLeft const):
+ (WebCore::SelectionGeometry::logicalWidth const):
+ (WebCore::SelectionGeometry::logicalTop const):
+ (WebCore::SelectionGeometry::logicalHeight const):
+ (WebCore::SelectionGeometry::behavior const):
+ (WebCore::SelectionGeometry::setBehavior):
+ (WebCore::SelectionGeometry::rect const): Deleted.
+ (WebCore::SelectionGeometry::setRect): Deleted.
+ * rendering/RenderImage.cpp:
+ (WebCore::RenderImage::collectSelectionGeometries):
+ * rendering/RenderLineBreak.cpp:
+ (WebCore::RenderLineBreak::collectSelectionGeometries):
+ * rendering/RenderObject.cpp:
+ (WebCore::RenderObject::collectSelectionGeometries):
+ (WebCore::adjustLineHeightOfSelectionGeometries):
+
+ When collecting selection geometry from renderers, avoid coalescing selection geometry when the
+ `SelectionGeometry` has `SelectionRenderingBehavior::UseIndividualQuads`.
+
+ (WebCore::coalesceSelectionGeometries):
+ (WebCore::RenderObject::collectSelectionGeometriesInternal):
+ * rendering/RenderText.cpp:
+ (WebCore::RenderText::collectSelectionGeometries):
+
2021-04-21 Tyler Wilcock <[email protected]>
CSSComputedStyleDeclaration.cpp should use C++ style comments
Modified: trunk/Source/WebCore/html/HTMLElement.cpp (276387 => 276388)
--- trunk/Source/WebCore/html/HTMLElement.cpp 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/html/HTMLElement.cpp 2021-04-21 20:11:31 UTC (rev 276388)
@@ -82,6 +82,10 @@
#include "ImageExtractionResult.h"
#endif
+#if PLATFORM(IOS_FAMILY)
+#include "SelectionGeometry.h"
+#endif
+
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLElement);
@@ -1372,6 +1376,15 @@
#endif // ENABLE(IMAGE_EXTRACTION)
+#if PLATFORM(IOS_FAMILY)
+
+SelectionRenderingBehavior HTMLElement::selectionRenderingBehavior(const Node* node)
+{
+ return isImageOverlayText(node) ? SelectionRenderingBehavior::UseIndividualQuads : SelectionRenderingBehavior::CoalesceBoundingRects;
+}
+
+#endif // PLATFORM(IOS_FAMILY)
+
} // namespace WebCore
#ifndef NDEBUG
Modified: trunk/Source/WebCore/html/HTMLElement.h (276387 => 276388)
--- trunk/Source/WebCore/html/HTMLElement.h 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/html/HTMLElement.h 2021-04-21 20:11:31 UTC (rev 276388)
@@ -43,6 +43,10 @@
enum class EnterKeyHint : uint8_t;
+#if PLATFORM(IOS_FAMILY)
+enum class SelectionRenderingBehavior : uint8_t;
+#endif
+
class HTMLElement : public StyledElement {
WTF_MAKE_ISO_ALLOCATED(HTMLElement);
public:
@@ -140,6 +144,10 @@
WEBCORE_EXPORT void updateWithImageExtractionResult(ImageExtractionResult&&);
#endif
+#if PLATFORM(IOS_FAMILY)
+ static SelectionRenderingBehavior selectionRenderingBehavior(const Node*);
+#endif
+
protected:
HTMLElement(const QualifiedName& tagName, Document&, ConstructionType);
Modified: trunk/Source/WebCore/platform/ios/SelectionGeometry.cpp (276387 => 276388)
--- trunk/Source/WebCore/platform/ios/SelectionGeometry.cpp 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/platform/ios/SelectionGeometry.cpp 2021-04-21 20:11:31 UTC (rev 276388)
@@ -26,13 +26,14 @@
#include "config.h"
#include "SelectionGeometry.h"
+#include "FloatQuad.h"
#include <wtf/text/TextStream.h>
namespace WebCore {
-SelectionGeometry::SelectionGeometry(const IntRect& rect, bool isHorizontal, int pageNumber)
- : m_rect(rect)
- , m_direction(TextDirection::LTR)
+SelectionGeometry::SelectionGeometry(const FloatQuad& quad, SelectionRenderingBehavior behavior, bool isHorizontal, int pageNumber)
+ : m_quad(quad)
+ , m_behavior(behavior)
, m_isHorizontal(isHorizontal)
, m_pageNumber(pageNumber)
{
@@ -39,8 +40,9 @@
}
// FIXME: We should move some of these arguments to an auxillary struct.
-SelectionGeometry::SelectionGeometry(const IntRect& rect, TextDirection direction, int minX, int maxX, int maxY, int lineNumber, bool isLineBreak, bool isFirstOnLine, bool isLastOnLine, bool containsStart, bool containsEnd, bool isHorizontal, bool isInFixedPosition, bool isRubyText, int pageNumber)
- : m_rect(rect)
+SelectionGeometry::SelectionGeometry(const FloatQuad& quad, SelectionRenderingBehavior behavior, TextDirection direction, int minX, int maxX, int maxY, int lineNumber, bool isLineBreak, bool isFirstOnLine, bool isLastOnLine, bool containsStart, bool containsEnd, bool isHorizontal, bool isInFixedPosition, bool isRubyText, int pageNumber)
+ : m_quad(quad)
+ , m_behavior(behavior)
, m_direction(direction)
, m_minX(minX)
, m_maxX(maxX)
@@ -60,42 +62,69 @@
void SelectionGeometry::setLogicalLeft(int left)
{
+ auto rect = this->rect();
if (m_isHorizontal)
- m_rect.setX(left);
+ rect.setX(left);
else
- m_rect.setY(left);
+ rect.setY(left);
+ setRect(rect);
}
void SelectionGeometry::setLogicalWidth(int width)
{
+ auto rect = this->rect();
if (m_isHorizontal)
- m_rect.setWidth(width);
+ rect.setWidth(width);
else
- m_rect.setHeight(width);
+ rect.setHeight(width);
+ setRect(rect);
}
void SelectionGeometry::setLogicalTop(int top)
{
+ auto rect = this->rect();
if (m_isHorizontal)
- m_rect.setY(top);
+ rect.setY(top);
else
- m_rect.setX(top);
+ rect.setX(top);
+ setRect(rect);
}
void SelectionGeometry::setLogicalHeight(int height)
{
+ auto rect = this->rect();
if (m_isHorizontal)
- m_rect.setHeight(height);
+ rect.setHeight(height);
else
- m_rect.setWidth(height);
+ rect.setWidth(height);
+ setRect(rect);
}
+IntRect SelectionGeometry::rect() const
+{
+ if (!m_cachedEnclosingRect)
+ m_cachedEnclosingRect = m_quad.enclosingBoundingBox();
+ return *m_cachedEnclosingRect;
+}
+
+void SelectionGeometry::setQuad(const FloatQuad& quad)
+{
+ m_quad = quad;
+ m_cachedEnclosingRect.reset();
+}
+
+void SelectionGeometry::setRect(const IntRect& rect)
+{
+ m_quad = FloatQuad { rect };
+ m_cachedEnclosingRect = rect;
+}
+
TextStream& operator<<(TextStream& stream, SelectionGeometry rect)
{
TextStream::GroupScope group(stream);
stream << "selection geometry";
- stream.dumpProperty("rect", rect.rect());
+ stream.dumpProperty("quad", rect.quad());
stream.dumpProperty("direction", isLeftToRightDirection(rect.direction()) ? "ltr" : "rtl");
stream.dumpProperty("min-x", rect.minX());
@@ -125,6 +154,9 @@
if (rect.isRubyText())
stream.dumpProperty("is ruby text", true);
+ if (rect.behavior() == SelectionRenderingBehavior::UseIndividualQuads)
+ stream.dumpProperty("using individual quads", true);
+
stream.dumpProperty("page number", rect.pageNumber());
return stream;
}
Modified: trunk/Source/WebCore/platform/ios/SelectionGeometry.h (276387 => 276388)
--- trunk/Source/WebCore/platform/ios/SelectionGeometry.h 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/platform/ios/SelectionGeometry.h 2021-04-21 20:11:31 UTC (rev 276388)
@@ -25,29 +25,40 @@
#pragma once
+#include "FloatQuad.h"
#include "IntRect.h"
#include "WritingMode.h"
#include <wtf/FastMalloc.h>
+#include <wtf/Optional.h>
namespace WebCore {
+enum class SelectionRenderingBehavior : uint8_t {
+ CoalesceBoundingRects,
+ UseIndividualQuads,
+};
+
class SelectionGeometry {
WTF_MAKE_FAST_ALLOCATED;
public:
- WEBCORE_EXPORT explicit SelectionGeometry(const IntRect&, bool isHorizontal, int columnNumber);
+ WEBCORE_EXPORT explicit SelectionGeometry(const FloatQuad&, SelectionRenderingBehavior, bool isHorizontal, int columnNumber);
// FIXME: We should move some of these arguments to an auxillary struct.
- SelectionGeometry(const IntRect&, TextDirection, int, int, int, int, bool, bool, bool, bool, bool, bool, bool, bool, int);
+ SelectionGeometry(const FloatQuad&, SelectionRenderingBehavior, TextDirection, int, int, int, int, bool, bool, bool, bool, bool, bool, bool, bool, int);
SelectionGeometry() = default;
~SelectionGeometry() = default;
- IntRect rect() const { return m_rect; }
+ FloatQuad quad() const { return m_quad; }
+ WEBCORE_EXPORT void setQuad(const FloatQuad&);
- int logicalLeft() const { return m_isHorizontal ? m_rect.x() : m_rect.y(); }
- int logicalWidth() const { return m_isHorizontal ? m_rect.width() : m_rect.height(); }
- int logicalTop() const { return m_isHorizontal ? m_rect.y() : m_rect.x(); }
- int logicalHeight() const { return m_isHorizontal ? m_rect.height() : m_rect.width(); }
+ WEBCORE_EXPORT IntRect rect() const;
+ WEBCORE_EXPORT void setRect(const IntRect&);
+ int logicalLeft() const { return m_isHorizontal ? rect().x() : rect().y(); }
+ int logicalWidth() const { return m_isHorizontal ? rect().width() : rect().height(); }
+ int logicalTop() const { return m_isHorizontal ? rect().y() : rect().x(); }
+ int logicalHeight() const { return m_isHorizontal ? rect().height() : rect().width(); }
+
TextDirection direction() const { return m_direction; }
int minX() const { return m_minX; }
int maxX() const { return m_maxX; }
@@ -62,9 +73,8 @@
bool isInFixedPosition() const { return m_isInFixedPosition; }
bool isRubyText() const { return m_isRubyText; }
int pageNumber() const { return m_pageNumber; }
+ SelectionRenderingBehavior behavior() const { return m_behavior; }
- void setRect(const IntRect& rect) { m_rect = rect; }
-
void setLogicalLeft(int);
void setLogicalWidth(int);
void setLogicalTop(int);
@@ -81,9 +91,11 @@
void setContainsStart(bool containsStart) { m_containsStart = containsStart; }
void setContainsEnd(bool containsEnd) { m_containsEnd = containsEnd; }
void setIsHorizontal(bool isHorizontal) { m_isHorizontal = isHorizontal; }
+ void setBehavior(SelectionRenderingBehavior behavior) { m_behavior = behavior; }
private:
- IntRect m_rect;
+ FloatQuad m_quad;
+ SelectionRenderingBehavior m_behavior { SelectionRenderingBehavior::CoalesceBoundingRects };
TextDirection m_direction { TextDirection::LTR };
int m_minX { 0 };
int m_maxX { 0 };
@@ -98,8 +110,22 @@
bool m_isInFixedPosition { false };
bool m_isRubyText { false };
int m_pageNumber { 0 };
+
+ mutable Optional<IntRect> m_cachedEnclosingRect;
};
WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, SelectionGeometry);
} // namespace WebCore
+
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::SelectionRenderingBehavior> {
+ using values = EnumValues<
+ WebCore::SelectionRenderingBehavior,
+ WebCore::SelectionRenderingBehavior::CoalesceBoundingRects,
+ WebCore::SelectionRenderingBehavior::UseIndividualQuads
+ >;
+};
+
+} // namespace WTF
Modified: trunk/Source/WebCore/rendering/RenderImage.cpp (276387 => 276388)
--- trunk/Source/WebCore/rendering/RenderImage.cpp 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/rendering/RenderImage.cpp 2021-04-21 20:11:31 UTC (rev 276388)
@@ -123,14 +123,14 @@
}
bool isFixed = false;
- IntRect absoluteBounds = localToAbsoluteQuad(FloatRect(imageRect), UseTransforms, &isFixed).enclosingBoundingBox();
- IntRect lineExtentBounds = localToAbsoluteQuad(FloatRect(lineExtentRect)).enclosingBoundingBox();
+ auto absoluteQuad = localToAbsoluteQuad(FloatRect(imageRect), UseTransforms, &isFixed);
+ auto lineExtentBounds = localToAbsoluteQuad(FloatRect(lineExtentRect)).enclosingBoundingBox();
if (!containingBlock->isHorizontalWritingMode())
lineExtentBounds = lineExtentBounds.transposedRect();
// FIXME: We should consider either making SelectionGeometry a struct or better organize its optional fields into
// an auxiliary struct to simplify its initialization.
- geometries.append(SelectionGeometry(absoluteBounds, containingBlock->style().direction(), lineExtentBounds.x(), lineExtentBounds.maxX(), lineExtentBounds.maxY(), 0, false /* line break */, isFirstOnLine, isLastOnLine, false /* contains start */, false /* contains end */, containingBlock->style().isHorizontalWritingMode(), isFixed, false /* ruby text */, view().pageNumberForBlockProgressionOffset(absoluteBounds.x())));
+ geometries.append(SelectionGeometry(absoluteQuad, SelectionRenderingBehavior::CoalesceBoundingRects, containingBlock->style().direction(), lineExtentBounds.x(), lineExtentBounds.maxX(), lineExtentBounds.maxY(), 0, false /* line break */, isFirstOnLine, isLastOnLine, false /* contains start */, false /* contains end */, containingBlock->style().isHorizontalWritingMode(), isFixed, false /* ruby text */, view().pageNumberForBlockProgressionOffset(absoluteQuad.enclosingBoundingBox().x())));
}
#endif
Modified: trunk/Source/WebCore/rendering/RenderLineBreak.cpp (276387 => 276388)
--- trunk/Source/WebCore/rendering/RenderLineBreak.cpp 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/rendering/RenderLineBreak.cpp 2021-04-21 20:11:31 UTC (rev 276388)
@@ -220,7 +220,7 @@
isLastOnLine = !containingBlock->containingBlock()->inlineBoxWrapper()->nextOnLineExists();
bool isFixed = false;
- IntRect absRect = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed).enclosingBoundingBox();
+ auto absoluteQuad = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed);
bool boxIsHorizontal = !is<SVGInlineTextBox>(run->legacyInlineBox()) ? run->isHorizontal() : !style().isVerticalWritingMode();
// If the containing block is an inline element, we want to check the inlineBoxWrapper orientation
// to determine the orientation of the block. In this case we also use the inlineBoxWrapper to
@@ -232,7 +232,7 @@
}
}
- rects.append(SelectionGeometry(absRect, run->direction(), extentsRect.x(), extentsRect.maxX(), extentsRect.maxY(), 0, run->isLineBreak(), isFirstOnLine, isLastOnLine, false, false, boxIsHorizontal, isFixed, containingBlock->isRubyText(), view().pageNumberForBlockProgressionOffset(absRect.x())));
+ rects.append(SelectionGeometry(absoluteQuad, HTMLElement::selectionRenderingBehavior(element()), run->direction(), extentsRect.x(), extentsRect.maxX(), extentsRect.maxY(), 0, run->isLineBreak(), isFirstOnLine, isLastOnLine, false, false, boxIsHorizontal, isFixed, containingBlock->isRubyText(), view().pageNumberForBlockProgressionOffset(absoluteQuad.enclosingBoundingBox().x())));
}
#endif
Modified: trunk/Source/WebCore/rendering/RenderObject.cpp (276387 => 276388)
--- trunk/Source/WebCore/rendering/RenderObject.cpp 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/rendering/RenderObject.cpp 2021-04-21 20:11:31 UTC (rev 276388)
@@ -737,9 +737,8 @@
child->absoluteQuads(quads);
}
- unsigned numberOfQuads = quads.size();
- for (unsigned i = 0; i < numberOfQuads; ++i)
- geometries.append(SelectionGeometry(quads[i].enclosingBoundingBox(), isHorizontalWritingMode(), view().pageNumberForBlockProgressionOffset(quads[i].enclosingBoundingBox().x())));
+ for (auto& quad : quads)
+ geometries.append(SelectionGeometry(quad, HTMLElement::selectionRenderingBehavior(node()), isHorizontalWritingMode(), view().pageNumberForBlockProgressionOffset(quad.enclosingBoundingBox().x())));
}
#endif
@@ -2124,6 +2123,8 @@
--i;
if (geometries[i].lineNumber())
break;
+ if (geometries[i].behavior() == SelectionRenderingBehavior::UseIndividualQuads)
+ continue;
geometries[i].setLineNumber(lineNumber);
geometries[i].setLogicalTop(lineTop);
geometries[i].setLogicalHeight(lineHeight);
@@ -2132,7 +2133,7 @@
static SelectionGeometry coalesceSelectionGeometries(const SelectionGeometry& original, const SelectionGeometry& previous)
{
- SelectionGeometry result(unionRect(previous.rect(), original.rect()), original.isHorizontal(), original.pageNumber());
+ SelectionGeometry result({ unionRect(previous.rect(), original.rect()) }, SelectionRenderingBehavior::CoalesceBoundingRects, original.isHorizontal(), original.pageNumber());
result.setDirection(original.containsStart() || original.containsEnd() ? original.direction() : previous.direction());
result.setContainsStart(previous.containsStart() || original.containsStart());
result.setContainsEnd(previous.containsEnd() || original.containsEnd());
@@ -2272,6 +2273,8 @@
for (size_t j = 1; j < numberOfGeometries; ++j) {
if (geometries[j].lineNumber() != geometries[j - 1].lineNumber())
continue;
+ if (geometries[j].behavior() == SelectionRenderingBehavior::UseIndividualQuads)
+ continue;
auto& previousRect = geometries[j - 1];
bool previousRectMayNotReachRightEdge = (previousRect.direction() == TextDirection::LTR && previousRect.containsEnd()) || (previousRect.direction() == TextDirection::RTL && previousRect.containsStart());
if (previousRectMayNotReachRightEdge)
@@ -2288,6 +2291,8 @@
auto& selectionGeometry = geometries[i];
if (!selectionGeometry.isLineBreak() && selectionGeometry.lineNumber() >= maxLineNumber)
continue;
+ if (selectionGeometry.behavior() == SelectionRenderingBehavior::UseIndividualQuads)
+ continue;
if (selectionGeometry.direction() == TextDirection::RTL && selectionGeometry.isFirstOnLine()) {
selectionGeometry.setLogicalWidth(selectionGeometry.logicalWidth() + selectionGeometry.logicalLeft() - selectionGeometry.minX());
selectionGeometry.setLogicalLeft(selectionGeometry.minX());
@@ -2309,6 +2314,15 @@
IntRect interiorUnionRect;
for (size_t i = 0; i < numberOfGeometries; ++i) {
auto& currentGeometry = result.geometries[i];
+ if (currentGeometry.behavior() == SelectionRenderingBehavior::UseIndividualQuads) {
+ // FIXME: We still probably want some way to coalesce quads, probably by projecting them onto rotated bounding rects
+ // and then checking whether the rotated bounding rects are overlapping and share the same rotation angle. Until then,
+ // we simply append all non-empty quads.
+ if (!currentGeometry.quad().isEmpty())
+ coalescedGeometries.append(currentGeometry);
+ continue;
+ }
+
if (currentGeometry.lineNumber() == 1) {
ASSERT(interiorUnionRect.isEmpty());
if (!coalescedGeometries.isEmpty()) {
@@ -2333,13 +2347,13 @@
// For iBooks, the interior lines may cross multiple horizontal pages.
interiorUnionRect.unite(currentGeometry.rect());
} else {
- coalescedGeometries.append(SelectionGeometry(interiorUnionRect, currentGeometry.isHorizontal(), currentGeometry.pageNumber()));
+ coalescedGeometries.append(SelectionGeometry({ interiorUnionRect }, SelectionRenderingBehavior::CoalesceBoundingRects, currentGeometry.isHorizontal(), currentGeometry.pageNumber()));
interiorUnionRect = currentGeometry.rect();
}
} else {
// Processing last line.
if (!interiorUnionRect.isEmpty()) {
- coalescedGeometries.append(SelectionGeometry(interiorUnionRect, currentGeometry.isHorizontal(), currentGeometry.pageNumber()));
+ coalescedGeometries.append(SelectionGeometry({ interiorUnionRect }, SelectionRenderingBehavior::CoalesceBoundingRects, currentGeometry.isHorizontal(), currentGeometry.pageNumber()));
interiorUnionRect = IntRect();
}
Modified: trunk/Source/WebCore/rendering/RenderText.cpp (276387 => 276388)
--- trunk/Source/WebCore/rendering/RenderText.cpp 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebCore/rendering/RenderText.cpp 2021-04-21 20:11:31 UTC (rev 276388)
@@ -369,7 +369,7 @@
bool containsEnd = run->start() <= end && run->end() >= end;
bool isFixed = false;
- IntRect absRect = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed).enclosingBoundingBox();
+ auto absoluteQuad = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed);
bool boxIsHorizontal = !is<SVGInlineTextBox>(run->legacyInlineBox()) ? run->isHorizontal() : !style().isVerticalWritingMode();
// If the containing block is an inline element, we want to check the inlineBoxWrapper orientation
// to determine the orientation of the block. In this case we also use the inlineBoxWrapper to
@@ -381,7 +381,7 @@
}
}
- rects.append(SelectionGeometry(absRect, run->direction(), extentsRect.x(), extentsRect.maxX(), extentsRect.maxY(), 0, run->isLineBreak(), isFirstOnLine, isLastOnLine, containsStart, containsEnd, boxIsHorizontal, isFixed, containingBlock->isRubyText(), view().pageNumberForBlockProgressionOffset(absRect.x())));
+ rects.append(SelectionGeometry(absoluteQuad, HTMLElement::selectionRenderingBehavior(textNode()), run->direction(), extentsRect.x(), extentsRect.maxX(), extentsRect.maxY(), 0, run->isLineBreak(), isFirstOnLine, isLastOnLine, containsStart, containsEnd, boxIsHorizontal, isFixed, containingBlock->isRubyText(), view().pageNumberForBlockProgressionOffset(absoluteQuad.enclosingBoundingBox().x())));
}
}
#endif
Modified: trunk/Source/WebKit/ChangeLog (276387 => 276388)
--- trunk/Source/WebKit/ChangeLog 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebKit/ChangeLog 2021-04-21 20:11:31 UTC (rev 276388)
@@ -1,3 +1,41 @@
+2021-04-21 Wenson Hsieh <[email protected]>
+
+ [iOS] Text selection in image overlays should not be limited to rectilinear quads
+ https://bugs.webkit.org/show_bug.cgi?id=224837
+ <rdar://76829981>
+
+ Reviewed by Tim Horton.
+
+ See WebCore ChangeLog for more details.
+
+ * Shared/WebCoreArgumentCoders.cpp:
+ (IPC::ArgumentCoder<SelectionGeometry>::encode):
+ (IPC::ArgumentCoder<SelectionGeometry>::decode):
+
+ Encode the `SelectionGeometry` by serializing a `FloatQuad` instead of an enclosing rect.
+
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (WebKit::operator==):
+ (-[WKContentView selectedTextRange]):
+ (-[WKContentView markedTextRange]):
+ * UIProcess/ios/WKTextSelectionRect.h:
+ * UIProcess/ios/WKTextSelectionRect.mm:
+ (-[WKTextSelectionRectCustomHandleInfo initWithFloatQuad:]):
+ (-[WKTextSelectionRectCustomHandleInfo bottomLeft]):
+ (-[WKTextSelectionRectCustomHandleInfo topLeft]):
+ (-[WKTextSelectionRectCustomHandleInfo bottomRight]):
+ (-[WKTextSelectionRectCustomHandleInfo topRight]):
+ (-[WKTextSelectionRect initWithCGRect:]):
+ (-[WKTextSelectionRect initWithSelectionGeometry:scaleFactor:]):
+ (-[WKTextSelectionRect _path]):
+ (-[WKTextSelectionRect _customHandleInfo]):
+
+ Implement SPI methods on `UITextSelectionRect` to render text selection UI using quads instead of rects.
+
+ (-[WKTextSelectionRect initWithSelectionGeometry:]): Deleted.
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::convertContentToRootView):
+
2021-04-21 Adrian Perez de Castro <[email protected]>
Non-unified build fixes, mid April 2021 edition
Modified: trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp (276387 => 276388)
--- trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp 2021-04-21 20:11:31 UTC (rev 276388)
@@ -1304,7 +1304,8 @@
void ArgumentCoder<SelectionGeometry>::encode(Encoder& encoder, const SelectionGeometry& selectionGeometry)
{
- encoder << selectionGeometry.rect();
+ encoder << selectionGeometry.quad();
+ encoder << selectionGeometry.behavior();
encoder << static_cast<uint32_t>(selectionGeometry.direction());
encoder << selectionGeometry.minX();
encoder << selectionGeometry.maxX();
@@ -1321,11 +1322,17 @@
Optional<SelectionGeometry> ArgumentCoder<SelectionGeometry>::decode(Decoder& decoder)
{
SelectionGeometry selectionGeometry;
- IntRect rect;
- if (!decoder.decode(rect))
+ FloatQuad quad;
+ if (!decoder.decode(quad))
return WTF::nullopt;
- selectionGeometry.setRect(rect);
+ selectionGeometry.setQuad(quad);
+ Optional<SelectionRenderingBehavior> behavior;
+ decoder >> behavior;
+ if (!behavior)
+ return WTF::nullopt;
+ selectionGeometry.setBehavior(*behavior);
+
uint32_t direction;
if (!decoder.decode(direction))
return WTF::nullopt;
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (276387 => 276388)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2021-04-21 20:11:31 UTC (rev 276388)
@@ -243,8 +243,17 @@
return false;
for (unsigned i = 0; i < a.selectionGeometries.size(); ++i) {
- if (a.selectionGeometries[i].rect() != b.selectionGeometries[i].rect())
+ auto& aGeometry = a.selectionGeometries[i];
+ auto& bGeometry = b.selectionGeometries[i];
+ auto behavior = aGeometry.behavior();
+ if (behavior != bGeometry.behavior())
return false;
+
+ if (behavior == WebCore::SelectionRenderingBehavior::CoalesceBoundingRects && aGeometry.rect() != bGeometry.rect())
+ return false;
+
+ if (behavior == WebCore::SelectionRenderingBehavior::UseIndividualQuads && aGeometry.quad() != bGeometry.quad())
+ return false;
}
}
@@ -4796,10 +4805,10 @@
{
}
-static NSArray<WKTextSelectionRect *> *wkTextSelectionRects(const Vector<WebCore::SelectionGeometry>& rects)
+static NSArray<WKTextSelectionRect *> *textSelectionRects(const Vector<WebCore::SelectionGeometry>& rects, CGFloat scaleFactor)
{
- return createNSArray(rects, [] (auto& rect) {
- return adoptNS([[WKTextSelectionRect alloc] initWithSelectionGeometry:rect]);
+ return createNSArray(rects, [scaleFactor] (auto& rect) {
+ return adoptNS([[WKTextSelectionRect alloc] initWithSelectionGeometry:rect scaleFactor:scaleFactor]);
}).autorelease();
}
@@ -4852,7 +4861,7 @@
auto caretStartRect = [self _scaledCaretRectForSelectionStart:_page->editorState().postLayoutData().caretRectAtStart];
auto caretEndRect = [self _scaledCaretRectForSelectionEnd:_page->editorState().postLayoutData().caretRectAtEnd];
- auto selectionRects = wkTextSelectionRects(_page->editorState().postLayoutData().selectionGeometries);
+ auto selectionRects = textSelectionRects(_page->editorState().postLayoutData().selectionGeometries, self._contentZoomScale);
auto selectedTextLength = editorState.postLayoutData().selectedTextLength;
_cachedSelectedTextRange = [WKTextRange textRangeWithState:!hasSelection isRange:isRange isEditable:isContentEditable startRect:caretStartRect endRect:caretEndRect selectionRects:selectionRects selectedTextLength:selectedTextLength];
return _cachedSelectedTextRange.get();
@@ -4902,7 +4911,7 @@
auto isContentEditable = editorState.isContentEditable;
auto caretStartRect = [self _scaledCaretRectForSelectionStart:unscaledCaretRectAtStart];
auto caretEndRect = [self _scaledCaretRectForSelectionEnd:unscaledCaretRectAtEnd];
- auto selectionRects = wkTextSelectionRects(postLayoutData.markedTextRects);
+ auto selectionRects = textSelectionRects(postLayoutData.markedTextRects, self._contentZoomScale);
auto selectedTextLength = postLayoutData.markedText.length();
return [WKTextRange textRangeWithState:!hasComposition isRange:isRange isEditable:isContentEditable startRect:caretStartRect endRect:caretEndRect selectionRects:selectionRects selectedTextLength:selectedTextLength];
}
Modified: trunk/Source/WebKit/UIProcess/ios/WKTextSelectionRect.h (276387 => 276388)
--- trunk/Source/WebKit/UIProcess/ios/WKTextSelectionRect.h 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebKit/UIProcess/ios/WKTextSelectionRect.h 2021-04-21 20:11:31 UTC (rev 276388)
@@ -34,7 +34,7 @@
@interface WKTextSelectionRect : UITextSelectionRect
- (instancetype)initWithCGRect:(CGRect)rect;
-- (instancetype)initWithSelectionGeometry:(const WebCore::SelectionGeometry&)selectionRect;
+- (instancetype)initWithSelectionGeometry:(const WebCore::SelectionGeometry&)selectionGeometry scaleFactor:(CGFloat)scaleFactor;
@end
Modified: trunk/Source/WebKit/UIProcess/ios/WKTextSelectionRect.mm (276387 => 276388)
--- trunk/Source/WebKit/UIProcess/ios/WKTextSelectionRect.mm 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebKit/UIProcess/ios/WKTextSelectionRect.mm 2021-04-21 20:11:31 UTC (rev 276388)
@@ -30,8 +30,52 @@
#import <WebCore/SelectionGeometry.h>
+#if HAVE(UI_TEXT_SELECTION_RECT_CUSTOM_HANDLE_INFO)
+
+@interface WKTextSelectionRectCustomHandleInfo : UITextSelectionRectCustomHandleInfo
+- (instancetype)initWithFloatQuad:(const WebCore::FloatQuad&)quad;
+@end
+
+@implementation WKTextSelectionRectCustomHandleInfo {
+ WebCore::FloatQuad _quad;
+}
+
+- (instancetype)initWithFloatQuad:(const WebCore::FloatQuad&)quad
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _quad = quad;
+ return self;
+}
+
+- (CGPoint)bottomLeft
+{
+ return _quad.p4();
+}
+
+- (CGPoint)topLeft
+{
+ return _quad.p1();
+}
+
+- (CGPoint)bottomRight
+{
+ return _quad.p3();
+}
+
+- (CGPoint)topRight
+{
+ return _quad.p2();
+}
+
+@end
+
+#endif // HAVE(UI_TEXT_SELECTION_RECT_CUSTOM_HANDLE_INFO)
+
@implementation WKTextSelectionRect {
WebCore::SelectionGeometry _selectionGeometry;
+ CGFloat _scaleFactor;
}
- (instancetype)initWithCGRect:(CGRect)rect
@@ -38,17 +82,53 @@
{
WebCore::SelectionGeometry selectionGeometry;
selectionGeometry.setRect(WebCore::enclosingIntRect(rect));
- return [self initWithSelectionGeometry:WTFMove(selectionGeometry)];
+ return [self initWithSelectionGeometry:WTFMove(selectionGeometry) scaleFactor:1];
}
-- (instancetype)initWithSelectionGeometry:(const WebCore::SelectionGeometry&)selectionRect
+- (instancetype)initWithSelectionGeometry:(const WebCore::SelectionGeometry&)selectionGeometry scaleFactor:(CGFloat)scaleFactor
{
if (!(self = [super init]))
return nil;
- _selectionGeometry = selectionRect;
+
+ _selectionGeometry = selectionGeometry;
+ _scaleFactor = scaleFactor;
return self;
}
+- (UIBezierPath *)_path
+{
+ if (_selectionGeometry.behavior() == WebCore::SelectionRenderingBehavior::CoalesceBoundingRects)
+ return nil;
+
+ auto selectionBounds = _selectionGeometry.rect();
+ auto quad = _selectionGeometry.quad();
+ quad.scale(_scaleFactor);
+ quad.move(-selectionBounds.x() * _scaleFactor, -selectionBounds.y() * _scaleFactor);
+
+ auto result = [UIBezierPath bezierPath];
+ [result moveToPoint:quad.p1()];
+ [result addLineToPoint:quad.p2()];
+ [result addLineToPoint:quad.p3()];
+ [result addLineToPoint:quad.p4()];
+ [result addLineToPoint:quad.p1()];
+ [result closePath];
+ return result;
+}
+
+#if HAVE(UI_TEXT_SELECTION_RECT_CUSTOM_HANDLE_INFO)
+
+- (WKTextSelectionRectCustomHandleInfo *)_customHandleInfo
+{
+ if (_selectionGeometry.behavior() == WebCore::SelectionRenderingBehavior::CoalesceBoundingRects)
+ return nil;
+
+ auto scaledQuad = _selectionGeometry.quad();
+ scaledQuad.scale(_scaleFactor);
+ return adoptNS([[WKTextSelectionRectCustomHandleInfo alloc] initWithFloatQuad:scaledQuad]).autorelease();
+}
+
+#endif // HAVE(UI_TEXT_SELECTION_RECT_CUSTOM_HANDLE_INFO)
+
- (CGRect)rect
{
return _selectionGeometry.rect();
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (276387 => 276388)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2021-04-21 20:06:42 UTC (rev 276387)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2021-04-21 20:11:31 UTC (rev 276388)
@@ -258,10 +258,16 @@
return needsLayout;
}
-static void convertContentToRootView(const FrameView& view, Vector<SelectionGeometry>& rects)
+static void convertContentToRootView(const FrameView& view, Vector<SelectionGeometry>& geometries)
{
- for (auto& rect : rects)
- rect.setRect(view.contentsToRootView(rect.rect()));
+ for (auto& geometry : geometries) {
+ auto convertedQuad = geometry.quad();
+ convertedQuad.setP1(view.contentsToRootView(convertedQuad.p1()));
+ convertedQuad.setP2(view.contentsToRootView(convertedQuad.p2()));
+ convertedQuad.setP3(view.contentsToRootView(convertedQuad.p3()));
+ convertedQuad.setP4(view.contentsToRootView(convertedQuad.p4()));
+ geometry.setQuad(convertedQuad);
+ }
}
void WebPage::getPlatformEditorState(Frame& frame, EditorState& result) const