- Revision
- 239931
- Author
- wenson_hs...@apple.com
- Date
- 2019-01-14 12:09:58 -0800 (Mon, 14 Jan 2019)
Log Message
[iOS] Expose SPI to access the current sentence boundary and selection state
https://bugs.webkit.org/show_bug.cgi?id=193398
<rdar://problem/45893108>
Reviewed by Dean Jackson.
Source/WebKit:
Expose SPI on WKWebView for internal clients to grab information about attributes at the current selection; so
far, this only includes whether the selection is a caret or a range, and whether or not the start of the
selection is at the start of a new sentence.
Test: EditorStateTests.ObserveSelectionAttributeChanges
* Shared/EditorState.cpp:
(WebKit::EditorState::PostLayoutData::encode const):
(WebKit::EditorState::PostLayoutData::decode):
* Shared/EditorState.h:
Add a new bit in EditorState on iOS to compute whether or not the start of the selection is at the start of a
new sentence. This is computed and set when sending post-layout data in `WebPageIOS.mm`.
* UIProcess/API/Cocoa/WKWebView.mm:
(selectionAttributes):
(-[WKWebView _didChangeEditorState]):
(-[WKWebView _selectionAttributes]):
Make the new SPI property support KVO by invoking `-willChangeValueForKey:` and `-didChangeValueForKey:`
whenever the selection attributes change.
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::platformEditorState const):
Tools:
Add an API test to verify that an SPI client can observe changes in the `@"_selectionAttributes"` key path on
WKWebView, and that inserting text, deleting, and changing the selection cause selection attributes to change as
expected.
* TestWebKitAPI/EditingTestHarness.h:
* TestWebKitAPI/EditingTestHarness.mm:
(-[EditingTestHarness moveBackward]):
(-[EditingTestHarness moveForward]):
(-[EditingTestHarness moveForwardAndExpectEditorStateWith:]):
Add a couple of new helper methods on EditingTestHarness.
* TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm:
(-[SelectionChangeObserver initWithWebView:]):
(-[SelectionChangeObserver webView]):
(-[SelectionChangeObserver observeValueForKeyPath:ofObject:change:context:]):
(-[SelectionChangeObserver currentSelectionAttributes]):
Modified Paths
Diff
Modified: trunk/Source/WebKit/ChangeLog (239930 => 239931)
--- trunk/Source/WebKit/ChangeLog 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Source/WebKit/ChangeLog 2019-01-14 20:09:58 UTC (rev 239931)
@@ -1,3 +1,37 @@
+2019-01-14 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [iOS] Expose SPI to access the current sentence boundary and selection state
+ https://bugs.webkit.org/show_bug.cgi?id=193398
+ <rdar://problem/45893108>
+
+ Reviewed by Dean Jackson.
+
+ Expose SPI on WKWebView for internal clients to grab information about attributes at the current selection; so
+ far, this only includes whether the selection is a caret or a range, and whether or not the start of the
+ selection is at the start of a new sentence.
+
+ Test: EditorStateTests.ObserveSelectionAttributeChanges
+
+ * Shared/EditorState.cpp:
+ (WebKit::EditorState::PostLayoutData::encode const):
+ (WebKit::EditorState::PostLayoutData::decode):
+ * Shared/EditorState.h:
+
+ Add a new bit in EditorState on iOS to compute whether or not the start of the selection is at the start of a
+ new sentence. This is computed and set when sending post-layout data in `WebPageIOS.mm`.
+
+ * UIProcess/API/Cocoa/WKWebView.mm:
+ (selectionAttributes):
+ (-[WKWebView _didChangeEditorState]):
+ (-[WKWebView _selectionAttributes]):
+
+ Make the new SPI property support KVO by invoking `-willChangeValueForKey:` and `-didChangeValueForKey:`
+ whenever the selection attributes change.
+
+ * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::platformEditorState const):
+
2019-01-14 Carlos Garcia Campos <cgar...@igalia.com>
Unreviewed. Update OptionsGTK.cmake and NEWS for 2.23.3 release
Modified: trunk/Source/WebKit/Shared/EditorState.cpp (239930 => 239931)
--- trunk/Source/WebKit/Shared/EditorState.cpp 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Source/WebKit/Shared/EditorState.cpp 2019-01-14 20:09:58 UTC (rev 239931)
@@ -132,6 +132,7 @@
encoder << hasPlainText;
encoder << elementIsTransparentOrFullyClipped;
encoder << caretColor;
+ encoder << atStartOfSentence;
#endif
#if PLATFORM(MAC)
encoder << candidateRequestStartPosition;
@@ -191,6 +192,8 @@
return false;
if (!decoder.decode(result.caretColor))
return false;
+ if (!decoder.decode(result.atStartOfSentence))
+ return false;
#endif
#if PLATFORM(MAC)
if (!decoder.decode(result.candidateRequestStartPosition))
Modified: trunk/Source/WebKit/Shared/EditorState.h (239930 => 239931)
--- trunk/Source/WebKit/Shared/EditorState.h 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Source/WebKit/Shared/EditorState.h 2019-01-14 20:09:58 UTC (rev 239931)
@@ -109,6 +109,7 @@
bool hasPlainText { false };
bool elementIsTransparentOrFullyClipped { false };
WebCore::Color caretColor;
+ bool atStartOfSentence { false };
#endif
#if PLATFORM(MAC)
uint64_t candidateRequestStartPosition { 0 };
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (239930 => 239931)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2019-01-14 20:09:58 UTC (rev 239931)
@@ -377,6 +377,7 @@
std::unique_ptr<WebKit::WebViewImpl> _impl;
RetainPtr<WKTextFinderClient> _textFinderClient;
#endif
+ _WKSelectionAttributes _selectionAttributes;
CGFloat _minimumEffectiveDeviceWidth;
}
@@ -1267,17 +1268,51 @@
};
}
+static _WKSelectionAttributes selectionAttributes(const WebKit::EditorState& editorState, _WKSelectionAttributes previousAttributes)
+{
+ _WKSelectionAttributes attributes = _WKSelectionAttributeNoSelection;
+ if (editorState.selectionIsNone)
+ return attributes;
+
+ if (editorState.selectionIsRange)
+ attributes |= _WKSelectionAttributeIsRange;
+ else
+ attributes |= _WKSelectionAttributeIsCaret;
+
+ if (!editorState.isMissingPostLayoutData) {
+#if PLATFORM(IOS_FAMILY)
+ if (editorState.postLayoutData().atStartOfSentence)
+ attributes |= _WKSelectionAttributeAtStartOfSentence;
+#endif
+ } else if (previousAttributes & _WKSelectionAttributeAtStartOfSentence)
+ attributes |= _WKSelectionAttributeAtStartOfSentence;
+
+ return attributes;
+}
+
- (void)_didChangeEditorState
{
- id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
+ auto newSelectionAttributes = selectionAttributes(_page->editorState(), _selectionAttributes);
+ if (_selectionAttributes != newSelectionAttributes) {
+ NSString *selectionAttributesKey = NSStringFromSelector(@selector(_selectionAttributes));
+ [self willChangeValueForKey:selectionAttributesKey];
+ _selectionAttributes = newSelectionAttributes;
+ [self didChangeValueForKey:selectionAttributesKey];
+ }
// FIXME: We should either rename -_webView:editorStateDidChange: to clarify that it's only intended for use when testing,
// or remove it entirely and use -_webView:didChangeFontAttributes: instead once text alignment is supported in the set of
// font attributes.
+ id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
[uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
}
+- (_WKSelectionAttributes)_selectionAttributes
+{
+ return _selectionAttributes;
+}
+
- (void)_showSafeBrowsingWarning:(const WebKit::SafeBrowsingWarning&)warning completionHandler:(CompletionHandler<void(Variant<WebKit::ContinueUnsafeLoad, URL>&&)>&&)completionHandler
{
_safeBrowsingWarning = adoptNS([[WKSafeBrowsingWarning alloc] initWithFrame:self.bounds safeBrowsingWarning:warning completionHandler:[weakSelf = WeakObjCPtr<WKWebView>(self), completionHandler = WTFMove(completionHandler)] (auto&& result) mutable {
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h (239930 => 239931)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2019-01-14 20:09:58 UTC (rev 239931)
@@ -63,6 +63,13 @@
_WKCaptureDeviceDisplay = 1 << 2,
} WK_API_AVAILABLE(macosx(10.13), ios(11.0));
+typedef NS_OPTIONS(NSUInteger, _WKSelectionAttributes) {
+ _WKSelectionAttributeNoSelection = 0,
+ _WKSelectionAttributeIsCaret = 1 << 0,
+ _WKSelectionAttributeIsRange = 1 << 1,
+ _WKSelectionAttributeAtStartOfSentence = 1 << 2,
+} WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+
#if TARGET_OS_IPHONE
typedef NS_ENUM(NSUInteger, _WKDragInteractionPolicy) {
@@ -220,6 +227,7 @@
- (IBAction)_takeFindStringFromSelection:(id)sender WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
@property (class, nonatomic, copy, setter=_setStringForFind:) NSString *_stringForFind WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic, readonly) _WKSelectionAttributes _selectionAttributes WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
#if TARGET_OS_IPHONE
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (239930 => 239931)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-01-14 20:09:58 UTC (rev 239931)
@@ -240,6 +240,7 @@
// FIXME: We should disallow replace when the string contains only CJ characters.
postLayoutData.isReplaceAllowed = result.isContentEditable && !result.isInPasswordField && !selectedText.isAllSpecialCharacters<isHTMLSpace>();
}
+ postLayoutData.atStartOfSentence = frame.selection().selectionAtSentenceStart();
postLayoutData.insideFixedPosition = startNodeIsInsideFixedPosition || endNodeIsInsideFixedPosition;
if (!selection.isNone()) {
if (m_focusedElement && m_focusedElement->renderer()) {
Modified: trunk/Tools/ChangeLog (239930 => 239931)
--- trunk/Tools/ChangeLog 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Tools/ChangeLog 2019-01-14 20:09:58 UTC (rev 239931)
@@ -1,3 +1,29 @@
+2019-01-14 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [iOS] Expose SPI to access the current sentence boundary and selection state
+ https://bugs.webkit.org/show_bug.cgi?id=193398
+ <rdar://problem/45893108>
+
+ Reviewed by Dean Jackson.
+
+ Add an API test to verify that an SPI client can observe changes in the `@"_selectionAttributes"` key path on
+ WKWebView, and that inserting text, deleting, and changing the selection cause selection attributes to change as
+ expected.
+
+ * TestWebKitAPI/EditingTestHarness.h:
+ * TestWebKitAPI/EditingTestHarness.mm:
+ (-[EditingTestHarness moveBackward]):
+ (-[EditingTestHarness moveForward]):
+ (-[EditingTestHarness moveForwardAndExpectEditorStateWith:]):
+
+ Add a couple of new helper methods on EditingTestHarness.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm:
+ (-[SelectionChangeObserver initWithWebView:]):
+ (-[SelectionChangeObserver webView]):
+ (-[SelectionChangeObserver observeValueForKeyPath:ofObject:change:context:]):
+ (-[SelectionChangeObserver currentSelectionAttributes]):
+
2019-01-14 Zalan Bujtas <za...@apple.com>
[LFC][BFC] Add basic box-sizing support.
Modified: trunk/Tools/TestWebKitAPI/EditingTestHarness.h (239930 => 239931)
--- trunk/Tools/TestWebKitAPI/EditingTestHarness.h 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Tools/TestWebKitAPI/EditingTestHarness.h 2019-01-14 20:09:58 UTC (rev 239931)
@@ -46,6 +46,8 @@
- (void)insertHTML:(NSString *)html;
- (void)selectAll;
- (void)deleteBackwards;
+- (void)moveBackward;
+- (void)moveForward;
- (void)insertParagraphAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
- (void)insertText:(NSString *)text andExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
- (void)insertHTML:(NSString *)html andExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries;
Modified: trunk/Tools/TestWebKitAPI/EditingTestHarness.mm (239930 => 239931)
--- trunk/Tools/TestWebKitAPI/EditingTestHarness.mm 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Tools/TestWebKitAPI/EditingTestHarness.mm 2019-01-14 20:09:58 UTC (rev 239931)
@@ -92,6 +92,16 @@
[self deleteBackwardAndExpectEditorStateWith:nil];
}
+- (void)moveBackward
+{
+ [self moveBackwardAndExpectEditorStateWith:nil];
+}
+
+- (void)moveForward
+{
+ [self moveForwardAndExpectEditorStateWith:nil];
+}
+
- (void)insertText:(NSString *)text andExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
{
[self _execCommand:@"InsertText" argument:text expectEntries:entries];
@@ -117,6 +127,11 @@
[self _execCommand:@"MoveWordBackward" argument:nil expectEntries:entries];
}
+- (void)moveForwardAndExpectEditorStateWith:(NSDictionary<NSString *, id> *)entries
+{
+ [self _execCommand:@"MoveForward" argument:nil expectEntries:entries];
+}
+
- (void)toggleBold
{
[self _execCommand:@"ToggleBold" argument:nil expectEntries:nil];
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm (239930 => 239931)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm 2019-01-14 19:21:50 UTC (rev 239930)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm 2019-01-14 20:09:58 UTC (rev 239931)
@@ -31,6 +31,7 @@
#import "PlatformUtilities.h"
#import "TestWKWebView.h"
#import <WebKit/WKWebViewPrivate.h>
+#import <wtf/Vector.h>
#if PLATFORM(IOS_FAMILY)
#import "UIKitSPI.h"
@@ -37,6 +38,52 @@
#import <UIKit/UIKit.h>
#endif
+static void* const SelectionAttributesObservationContext = (void*)&SelectionAttributesObservationContext;
+
+@interface SelectionChangeObserver : NSObject
+- (instancetype)initWithWebView:(TestWKWebView *)webView;
+@property (nonatomic, readonly) TestWKWebView *webView;
+@property (nonatomic, readonly) _WKSelectionAttributes currentSelectionAttributes;
+@end
+
+@implementation SelectionChangeObserver {
+ RetainPtr<TestWKWebView> _webView;
+ Vector<_WKSelectionAttributes> _observedSelectionAttributes;
+}
+
+- (instancetype)initWithWebView:(TestWKWebView *)webView
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _webView = webView;
+ [_webView addObserver:self forKeyPath:@"_selectionAttributes" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:SelectionAttributesObservationContext];
+ return self;
+}
+
+- (TestWKWebView *)webView
+{
+ return _webView.get();
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
+{
+ if (context == SelectionAttributesObservationContext) {
+ if (!_observedSelectionAttributes.isEmpty())
+ EXPECT_EQ(_observedSelectionAttributes.last(), [change[NSKeyValueChangeOldKey] unsignedIntValue]);
+ _observedSelectionAttributes.append([change[NSKeyValueChangeNewKey] unsignedIntValue]);
+ return;
+ }
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+- (_WKSelectionAttributes)currentSelectionAttributes
+{
+ return _observedSelectionAttributes.isEmpty() ? _WKSelectionAttributeNoSelection : _observedSelectionAttributes.last();
+}
+
+@end
+
namespace TestWebKitAPI {
static RetainPtr<EditingTestHarness> setUpEditorStateTestHarness()
@@ -312,8 +359,48 @@
auto cgRedColor = adoptCF(CGColorCreateCopyByMatchingToColorSpace(colorSpace.get(), kCGRenderingIntentDefault, redColor.CGColor, NULL));
EXPECT_TRUE(CGColorEqualToColor(cgInsertionPointColor.get(), cgRedColor.get()));
}
-#endif
+TEST(EditorStateTests, ObserveSelectionAttributeChanges)
+{
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ auto editor = adoptNS([[EditingTestHarness alloc] initWithWebView:webView.get()]);
+ [webView _setEditable:YES];
+ [webView synchronouslyLoadHTMLString:@"<body></body>"];
+
+ auto observer = adoptNS([[SelectionChangeObserver alloc] initWithWebView:webView.get()]);
+
+ [webView evaluateJavaScript:@"document.body.focus()" completionHandler:nil];
+ [webView waitForNextPresentationUpdate];
+ EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
+
+ [editor insertText:@"Hello"];
+ EXPECT_EQ(_WKSelectionAttributeIsCaret, [observer currentSelectionAttributes]);
+
+ [editor insertText:@"."];
+ EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
+
+ [editor moveBackward];
+ EXPECT_EQ(_WKSelectionAttributeIsCaret, [observer currentSelectionAttributes]);
+
+ [editor moveForward];
+ EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
+
+ [editor deleteBackwards];
+ EXPECT_EQ(_WKSelectionAttributeIsCaret, [observer currentSelectionAttributes]);
+
+ [editor insertParagraph];
+ EXPECT_EQ(_WKSelectionAttributeIsCaret | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
+
+ [editor selectAll];
+ EXPECT_EQ(_WKSelectionAttributeIsRange | _WKSelectionAttributeAtStartOfSentence, [observer currentSelectionAttributes]);
+
+ [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
+ [webView waitForNextPresentationUpdate];
+ EXPECT_EQ(_WKSelectionAttributeNoSelection, [observer currentSelectionAttributes]);
+}
+
+#endif // PLATFORM(IOS_FAMILY)
+
} // namespace TestWebKitAPI
#endif // WK_API_ENABLED