Skip to content
Permalink
Browse files
Allow text selection to flip.
https://bugs.webkit.org/show_bug.cgi?id=231234
rdar://83889188

Reviewed by Wenson Hsieh.

Source/WebKit:

In order to bring webkit more in line with UIKit, allow text selection to flip.
This also requires a UIKit change tracked in rdar://83788439
This is currently guarded behind an off-by-default flag so that we can
test this new behavior before turning it on by default.

* Platform/spi/ios/UIKitSPI.h:
* Shared/ios/GestureTypes.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(toUIWKSelectionFlags):
(toSelectionFlags):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::updatePreferences):
* WebProcess/WebPage/WebPage.h:
(WebKit::WebPage::selectionFlippingEnabled const):
(WebKit::WebPage::setSelectionFlippingEnabled):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::rangeForPointInRootViewCoordinates):
(WebKit::WebPage::updateSelectionWithTouches):

Source/WTF:

Add an internal flag to guard text selection flipping while
we test to make sure this is reasonable to turn on everywhere.

* Scripts/Preferences/WebPreferencesInternal.yaml:


Canonical link: https://commits.webkit.org/242571@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@283619 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
megangardner committed Oct 6, 2021
1 parent 1e0c954 commit 1a22c5163f5e022a81216bbfe61c97513dd94e80
Showing 9 changed files with 118 additions and 32 deletions.
@@ -1,3 +1,16 @@
2021-10-06 Megan Gardner <megan_gardner@apple.com>

Allow text selection to flip.
https://bugs.webkit.org/show_bug.cgi?id=231234
rdar://83889188

Reviewed by Wenson Hsieh.

Add an internal flag to guard text selection flipping while
we test to make sure this is reasonable to turn on everywhere.

* Scripts/Preferences/WebPreferencesInternal.yaml:

2021-10-05 Tim Horton <timothy_horton@apple.com>

Add an alternate style for form controls, and implement it for checkboxes and radio buttons
@@ -681,6 +681,15 @@ SelectionAcrossShadowBoundariesEnabled:
default: true
WebCore:
default: true

SelectionFlippingEnabled:
type: bool
humanReadableName: "Selection Flipping"
humanReadableDescription: "Enable Selection Flipping"
webcoreBinding: none
defaultValue:
WebKit:
default: false

# FIXME: This is not implemented for WebKitLegacy, so should be excluded from WebKitLegacy entirely.
ServiceWorkersEnabled:
@@ -1,3 +1,30 @@
2021-10-06 Megan Gardner <megan_gardner@apple.com>

Allow text selection to flip.
https://bugs.webkit.org/show_bug.cgi?id=231234
rdar://83889188

Reviewed by Wenson Hsieh.

In order to bring webkit more in line with UIKit, allow text selection to flip.
This also requires a UIKit change tracked in rdar://83788439
This is currently guarded behind an off-by-default flag so that we can
test this new behavior before turning it on by default.

* Platform/spi/ios/UIKitSPI.h:
* Shared/ios/GestureTypes.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(toUIWKSelectionFlags):
(toSelectionFlags):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::updatePreferences):
* WebProcess/WebPage/WebPage.h:
(WebKit::WebPage::selectionFlippingEnabled const):
(WebKit::WebPage::setSelectionFlippingEnabled):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::rangeForPointInRootViewCoordinates):
(WebKit::WebPage::updateSelectionWithTouches):

2021-10-06 Chris Dumez <cdumez@apple.com>

IPC SimpleArgumentEncoder should static_assert(std::is_trivially_copyable<T>)
@@ -139,6 +139,9 @@
#import <UIKit/UIPointerStyle_Private.h>
#endif

// FIXME: STAGING for rdar://75546704 Remove later.
#define UIWKSelectionFlipped 2

#else // USE(APPLE_INTERNAL_SDK)

#if ENABLE(DRAG_SUPPORT)
@@ -682,6 +685,7 @@ typedef NS_ENUM(NSInteger, UIWKSelectionTouch) {
typedef NS_ENUM(NSInteger, UIWKSelectionFlags) {
UIWKNone = 0,
UIWKWordIsNearTap = 1,
UIWKSelectionFlipped = 2,
UIWKPhraseBoundaryChanged = 4,
};

@@ -65,7 +65,8 @@ enum class SheetAction : bool {

enum SelectionFlags : uint8_t {
WordIsNearTap = 1 << 0,
PhraseBoundaryChanged = 1 << 1,
SelectionFlipped = 1 << 1,
PhraseBoundaryChanged = 1 << 2,
};

enum class RespectSelectionAnchor : bool {
@@ -107,6 +108,7 @@ template<> struct EnumTraits<WebKit::SelectionFlags> {
using values = EnumValues<
WebKit::SelectionFlags,
WebKit::SelectionFlags::WordIsNearTap,
WebKit::SelectionFlags::SelectionFlipped,
WebKit::SelectionFlags::PhraseBoundaryChanged
>;
};
@@ -4273,6 +4273,8 @@ static inline UIWKSelectionFlags toUIWKSelectionFlags(OptionSet<WebKit::Selectio
NSInteger uiFlags = UIWKNone;
if (flags.contains(WebKit::WordIsNearTap))
uiFlags |= UIWKWordIsNearTap;
if (flags.contains(WebKit::SelectionFlipped))
uiFlags |= UIWKSelectionFlipped;
if (flags.contains(WebKit::PhraseBoundaryChanged))
uiFlags |= UIWKPhraseBoundaryChanged;

@@ -4284,6 +4286,8 @@ static inline UIWKSelectionFlags toUIWKSelectionFlags(OptionSet<WebKit::Selectio
OptionSet<WebKit::SelectionFlags> flags;
if (uiFlags & UIWKWordIsNearTap)
flags.add(WebKit::WordIsNearTap);
if (uiFlags & UIWKSelectionFlipped)
flags.add(WebKit::SelectionFlipped);
if (uiFlags & UIWKPhraseBoundaryChanged)
flags.add(WebKit::PhraseBoundaryChanged);
return flags;
@@ -3981,6 +3981,8 @@ void WebPage::updatePreferences(const WebPreferencesStore& store)

#if PLATFORM(COCOA)
m_pdfPluginEnabled = store.getBoolValueForKey(WebPreferencesKey::pdfPluginEnabledKey());

m_selectionFlippingEnabled = store.getBoolValueForKey(WebPreferencesKey::selectionFlippingEnabledKey());
#endif
#if ENABLE(PAYMENT_REQUEST)
settings.setPaymentRequestEnabled(store.getBoolValueForKey(WebPreferencesKey::applePayEnabledKey()));
@@ -1133,6 +1133,9 @@ class WebPage : public API::ObjectImpl<API::Object::Type::BundlePage>, public IP
#if PLATFORM(COCOA)
bool pdfPluginEnabled() const { return m_pdfPluginEnabled; }
void setPDFPluginEnabled(bool enabled) { m_pdfPluginEnabled = enabled; }

bool selectionFlippingEnabled() const { return m_selectionFlippingEnabled; }
void setSelectionFlippingEnabled(bool enabled) { m_selectionFlippingEnabled = enabled; }

NSDictionary *dataDetectionContext() const { return m_dataDetectionContext.get(); }
#endif
@@ -1996,6 +1999,7 @@ class WebPage : public API::ObjectImpl<API::Object::Type::BundlePage>, public IP
#if PLATFORM(COCOA)
bool m_pdfPluginEnabled { false };
bool m_hasCachedWindowFrame { false };
bool m_selectionFlippingEnabled { false };

// The frame of the containing window in screen coordinates.
WebCore::FloatRect m_windowFrameInScreenCoordinates;
@@ -161,6 +161,8 @@

namespace WebKit {

enum class SelectionWasFlipped : bool { No, Yes };

// FIXME: Unclear if callers in this file are correctly choosing which of these two functions to use.

static String plainTextForContext(const SimpleRange& range)
@@ -1503,34 +1505,36 @@ static bool insideImageOverlay(const VisiblePosition& position)
completionHandler(point, gestureType, gestureState, flags);
}

static std::optional<SimpleRange> rangeForPointInRootViewCoordinates(Frame& frame, const IntPoint& pointInRootViewCoordinates, bool baseIsStart)
static std::pair<std::optional<SimpleRange>, SelectionWasFlipped> rangeForPointInRootViewCoordinates(Frame& frame, const IntPoint& pointInRootViewCoordinates, bool baseIsStart, bool selectionFlippingEnabled)
{
VisibleSelection existingSelection = frame.selection().selection();
VisiblePosition selectionStart = existingSelection.visibleStart();
VisiblePosition selectionEnd = existingSelection.visibleEnd();

auto pointInDocument = frame.view()->rootViewToContents(pointInRootViewCoordinates);

auto node = selectionStart.deepEquivalent().containerNode();
if (node && node->renderStyle() && node->renderStyle()->isVerticalWritingMode()) {
if (baseIsStart) {
int startX = selectionStart.absoluteCaretBounds().center().x();
if (pointInDocument.x() > startX)
pointInDocument.setX(startX);
} else {
int endX = selectionEnd.absoluteCaretBounds().center().x();
if (pointInDocument.x() < endX)
pointInDocument.setX(endX);
}
} else {
if (baseIsStart) {
int startY = selectionStart.absoluteCaretBounds().center().y();
if (pointInDocument.y() < startY)
pointInDocument.setY(startY);
if (!selectionFlippingEnabled) {
auto node = selectionStart.deepEquivalent().containerNode();
if (node && node->renderStyle() && node->renderStyle()->isVerticalWritingMode()) {
if (baseIsStart) {
int startX = selectionStart.absoluteCaretBounds().center().x();
if (pointInDocument.x() > startX)
pointInDocument.setX(startX);
} else {
int endX = selectionEnd.absoluteCaretBounds().center().x();
if (pointInDocument.x() < endX)
pointInDocument.setX(endX);
}
} else {
int endY = selectionEnd.absoluteCaretBounds().center().y();
if (pointInDocument.y() > endY)
pointInDocument.setY(endY);
if (baseIsStart) {
int startY = selectionStart.absoluteCaretBounds().center().y();
if (pointInDocument.y() < startY)
pointInDocument.setY(startY);
} else {
int endY = selectionEnd.absoluteCaretBounds().center().y();
if (pointInDocument.y() > endY)
pointInDocument.setY(endY);
}
}
}

@@ -1542,36 +1546,49 @@ static bool insideImageOverlay(const VisiblePosition& position)

RefPtr targetNode = hitTest.targetNode();
if (targetNode && !HTMLElement::shouldExtendSelectionToTargetNode(*targetNode, existingSelection))
return std::nullopt;
return { std::nullopt, SelectionWasFlipped::No };

std::optional<SimpleRange> range;
VisiblePosition result;
std::optional<SimpleRange> range;
SelectionWasFlipped selectionFlipped = SelectionWasFlipped::No;

if (targetNode)
result = frame.eventHandler().selectionExtentRespectingEditingBoundary(frame.selection().selection(), hitTest.localPoint(), targetNode.get()).deepEquivalent();
else
result = frame.visiblePositionForPoint(pointInDocument).deepEquivalent();

if (baseIsStart) {
if (result <= selectionStart)
bool flipSelection = result < selectionStart;

if ((flipSelection && !selectionFlippingEnabled) || result == selectionStart)
result = selectionStart.next();
else if (RefPtr containerNode = selectionStart.deepEquivalent().containerNode(); containerNode && targetNode && &containerNode->treeScope() != &targetNode->treeScope())
result = VisibleSelection::adjustPositionForEnd(result.deepEquivalent(), containerNode.get());

range = makeSimpleRange(selectionStart, result);

if (selectionFlippingEnabled && flipSelection) {
range = makeSimpleRange(result, selectionStart);
selectionFlipped = SelectionWasFlipped::Yes;
} else
range = makeSimpleRange(selectionStart, result);
} else {
if (selectionEnd <= result)
bool flipSelection = selectionEnd < result;

if ((flipSelection && !selectionFlippingEnabled) || result == selectionEnd)
result = selectionEnd.previous();
else if (RefPtr containerNode = selectionEnd.deepEquivalent().containerNode(); containerNode && targetNode && &containerNode->treeScope() != &targetNode->treeScope())
result = VisibleSelection::adjustPositionForStart(result.deepEquivalent(), containerNode.get());

range = makeSimpleRange(result, selectionEnd);
if (selectionFlippingEnabled && flipSelection) {
range = makeSimpleRange(selectionEnd, result);
selectionFlipped = SelectionWasFlipped::Yes;
} else
range = makeSimpleRange(result, selectionEnd);
}

if (range && HTMLElement::isInsideImageOverlay(*range))
return expandForImageOverlay(*range);
return { expandForImageOverlay(*range), SelectionWasFlipped::No };

return range;
return { range, selectionFlipped };
}

static std::optional<SimpleRange> rangeAtWordBoundaryForPosition(Frame* frame, const VisiblePosition& position, bool baseIsStart)
@@ -1743,6 +1760,7 @@ static bool insideImageOverlay(const VisiblePosition& position)

std::optional<SimpleRange> range;
OptionSet<SelectionFlags> flags;
SelectionWasFlipped selectionFlipped = SelectionWasFlipped::No;

switch (selectionTouch) {
case SelectionTouch::Started:
@@ -1753,7 +1771,7 @@ static bool insideImageOverlay(const VisiblePosition& position)
if (frame->selection().selection().isContentEditable())
range = makeSimpleRange(closestWordBoundaryForPosition(position));
else
range = rangeForPointInRootViewCoordinates(frame, point, baseIsStart);
std::tie(range, selectionFlipped) = rangeForPointInRootViewCoordinates(frame, point, baseIsStart, selectionFlippingEnabled());
break;

case SelectionTouch::EndedMovingForward:
@@ -1762,12 +1780,15 @@ static bool insideImageOverlay(const VisiblePosition& position)
break;

case SelectionTouch::Moved:
range = rangeForPointInRootViewCoordinates(frame, point, baseIsStart);
std::tie(range, selectionFlipped) = rangeForPointInRootViewCoordinates(frame, point, baseIsStart, selectionFlippingEnabled());
break;
}

if (range)
frame->selection().setSelectedRange(range, position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);

if (selectionFlipped == SelectionWasFlipped::Yes)
flags = SelectionFlipped;

completionHandler(point, selectionTouch, flags);
}

0 comments on commit 1a22c51

Please sign in to comment.