[iOS DnD] Drag caret rect is incorrectly computed when dropping in editable content...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Jun 2017 16:08:21 +0000 (16:08 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Jun 2017 16:08:21 +0000 (16:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174017
<rdar://problem/32959782>

Reviewed by Simon Fraser.

Source/WebCore:

We're currenly computing the drag caret rect (for the purposes of presentation at the client layers)
incorrectly, in per-frame document coordinates instead of root view coordinates in the mainframe. This means
drag caret geometry from embedded iframes in the document will show up in the content view with a rect in the
coordinate space of the iframe.

To fix this, we need to convert the drag caret rect to root view coordinates. This patch teaches
DragCaretController to do this, and tweaks WebKit/WebKit2 to use caretRectInRootViewCoordinates.

Test: DataInteractionTests.ExternalSourcePlainTextToIFrame

* editing/FrameSelection.cpp:
(WebCore::DragCaretController::caretRectInRootViewCoordinates):
* editing/FrameSelection.h:

Source/WebKit/mac:

Use root view coordinates when computing the drag caret rect.

* WebView/WebView.mm:
(-[WebView _dataInteractionCaretRect]):

Source/WebKit2:

Send the drag caret rect in root view coordinates over to the UI process.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _dragCaretRect]):
* UIProcess/API/Cocoa/WKWebViewPrivate.h:

Add basic test plumbing to fetch the current drag caret rect.

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::performDragControllerAction):

Tools:

Add a new test verifying that the drag caret is visually contained within the bounds of an iframe. Before these
changes, the caret would appear outside of the iframe.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKit2Cocoa/contenteditable-in-iframe.html: Added.
* TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
(checkDragCaretRectIsContainedInRect):
(TestWebKitAPI::TEST):
* TestWebKitAPI/ios/DataInteractionSimulator.h:
* TestWebKitAPI/ios/DataInteractionSimulator.mm:
(-[DataInteractionSimulator _resetSimulatedState]):
(-[DataInteractionSimulator _concludeDataInteractionAndPerformOperationIfNecessary]):
(-[DataInteractionSimulator _advanceProgress]):
(-[DataInteractionSimulator lastKnownDragCaretRect]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@218998 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebCore/ChangeLog
Source/WebCore/editing/FrameSelection.cpp
Source/WebCore/editing/FrameSelection.h
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebView/WebView.mm
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/contenteditable-in-iframe.html [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm
Tools/TestWebKitAPI/ios/DataInteractionSimulator.h
Tools/TestWebKitAPI/ios/DataInteractionSimulator.mm

index 98d25b2624b1c94d748c060e3b86d87d901b110e..dd4b99e23ad3c786a97ce6d2f1e6ab5cf1b512d5 100644 (file)
@@ -1,3 +1,25 @@
+2017-06-30  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS DnD] Drag caret rect is incorrectly computed when dropping in editable content in iframes
+        https://bugs.webkit.org/show_bug.cgi?id=174017
+        <rdar://problem/32959782>
+
+        Reviewed by Simon Fraser.
+
+        We're currenly computing the drag caret rect (for the purposes of presentation at the client layers)
+        incorrectly, in per-frame document coordinates instead of root view coordinates in the mainframe. This means
+        drag caret geometry from embedded iframes in the document will show up in the content view with a rect in the
+        coordinate space of the iframe.
+
+        To fix this, we need to convert the drag caret rect to root view coordinates. This patch teaches
+        DragCaretController to do this, and tweaks WebKit/WebKit2 to use caretRectInRootViewCoordinates.
+
+        Test: DataInteractionTests.ExternalSourcePlainTextToIFrame
+
+        * editing/FrameSelection.cpp:
+        (WebCore::DragCaretController::caretRectInRootViewCoordinates):
+        * editing/FrameSelection.h:
+
 2017-06-30  Sam Weinig  <sam@webkit.org>
 
         [WebIDL] Replace use of __is_polymorphic with standard std::is_polymorphic<>::value
index c777b92d8983d0d89be7fd6713959bd95b7ee14c..2e06e17be4422853d1f6216891bb63b7e92bfb04 100644 (file)
@@ -101,6 +101,19 @@ bool DragCaretController::isContentRichlyEditable() const
     return isRichlyEditablePosition(m_position.deepEquivalent());
 }
 
+IntRect DragCaretController::caretRectInRootViewCoordinates() const
+{
+    if (!hasCaret())
+        return { };
+
+    if (auto* document = m_position.deepEquivalent().document()) {
+        if (auto* documentView = document->view())
+            return documentView->contentsToRootView(m_position.absoluteCaretBounds());
+    }
+
+    return { };
+}
+
 static inline bool shouldAlwaysUseDirectionalSelection(Frame* frame)
 {
     return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
index 68e5dc1b2e57d5d54cf9bc8deae39a7745140c0b..4b500f8301480ee8df3869ddd683c3b98d210549 100644 (file)
@@ -103,6 +103,7 @@ public:
     const VisiblePosition& caretPosition() { return m_position; }
     void setCaretPosition(const VisiblePosition&);
     void clear() { setCaretPosition(VisiblePosition()); }
+    WEBCORE_EXPORT IntRect caretRectInRootViewCoordinates() const;
 
     void nodeWillBeRemoved(Node&);
 
index 3269018f6fd858a42e0da1323cb199e40d4069d0..f3bf5803818d94e206b51c5b7741e1984eebf1b4 100644 (file)
@@ -1,3 +1,16 @@
+2017-06-30  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS DnD] Drag caret rect is incorrectly computed when dropping in editable content in iframes
+        https://bugs.webkit.org/show_bug.cgi?id=174017
+        <rdar://problem/32959782>
+
+        Reviewed by Simon Fraser.
+
+        Use root view coordinates when computing the drag caret rect.
+
+        * WebView/WebView.mm:
+        (-[WebView _dataInteractionCaretRect]):
+
 2017-06-28  Simon Fraser  <simon.fraser@apple.com>
 
         Mark the GraphicsContext as accelerated when the WebHTMLView's layer is drawing asynchronously
index 7826fb478ee30549185e5774eea756c2e1522ecb..aea54e485af760cf72b6a8b8454ababd974b90aa 100644 (file)
@@ -1849,7 +1849,7 @@ static void WebKitInitializeGamepadProviderIfNecessary()
 - (CGRect)_dataInteractionCaretRect
 {
     if (auto* page = _private->page)
-        return page->dragCaretController().caretPosition().absoluteCaretBounds();
+        return page->dragCaretController().caretRectInRootViewCoordinates();
 
     return { };
 }
index dea7acb3d5ff1c034bd535bcbff5a54b08b18613..84c53c80574da1adf5588609283fbb7c33f0a067 100644 (file)
@@ -1,3 +1,22 @@
+2017-06-30  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS DnD] Drag caret rect is incorrectly computed when dropping in editable content in iframes
+        https://bugs.webkit.org/show_bug.cgi?id=174017
+        <rdar://problem/32959782>
+
+        Reviewed by Simon Fraser.
+
+        Send the drag caret rect in root view coordinates over to the UI process.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _dragCaretRect]):
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+
+        Add basic test plumbing to fetch the current drag caret rect.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::performDragControllerAction):
+
 2017-06-30  Youenn Fablet  <youenn@apple.com>
 
         Support PeerConnectionStates::BundlePolicy::MaxBundle when setting rtc configuration
index af504ea3cae0c3c73c7177a9a7a702a1066e6404..dc93d8dcc8ab9f33964a4bf8d09f09659e2a3515 100644 (file)
@@ -5650,6 +5650,15 @@ static WebCore::UserInterfaceLayoutDirection toUserInterfaceLayoutDirection(UISe
 #endif
 }
 
+- (CGRect)_dragCaretRect
+{
+#if ENABLE(DRAG_SUPPORT)
+    return _page->currentDragCaretRect();
+#else
+    return CGRectZero;
+#endif
+}
+
 - (void)_simulateLongPressActionAtLocation:(CGPoint)location
 {
     [_contentView _simulateLongPressActionAtLocation:location];
index a2bb23cfb7f52a36baa43fdb16e8ed62868966a0..dbe12a01eb23947e4a49eee3a18afd9076b6798b 100644 (file)
@@ -358,6 +358,8 @@ typedef NS_ENUM(NSInteger, _WKImmediateActionType) {
 - (void)_simulatePrepareForDataInteractionSession:(id)session completion:(dispatch_block_t)completion WK_API_AVAILABLE(ios(WK_IOS_TBA));
 - (void)_simulateLongPressActionAtLocation:(CGPoint)location;
 
+@property (nonatomic, readonly) CGRect _dragCaretRect WK_API_AVAILABLE(ios(WK_IOS_TBA));
+
 - (_WKDraggableElementInfo *)_draggableElementAtPosition:(CGPoint)position WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 - (void)_requestDraggableElementAtPosition:(CGPoint)position completionBlock:(void (^)(_WKDraggableElementInfo *))block WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
index 6962081511f0a455be4b413bae2b0c871b8b6c42..fcce7dae37ef2271113f1803cb276c2455c7671a 100644 (file)
@@ -3590,13 +3590,13 @@ void WebPage::performDragControllerAction(uint64_t action, const WebCore::DragDa
     switch (action) {
     case DragControllerActionEntered: {
         DragOperation resolvedDragOperation = m_page->dragController().dragEntered(dragData);
-        send(Messages::WebPageProxy::DidPerformDragControllerAction(resolvedDragOperation, m_page->dragController().mouseIsOverFileInput(), m_page->dragController().numberOfItemsToBeAccepted(), m_page->dragCaretController().caretPosition().absoluteCaretBounds()));
+        send(Messages::WebPageProxy::DidPerformDragControllerAction(resolvedDragOperation, m_page->dragController().mouseIsOverFileInput(), m_page->dragController().numberOfItemsToBeAccepted(), m_page->dragCaretController().caretRectInRootViewCoordinates()));
         break;
 
     }
     case DragControllerActionUpdated: {
         DragOperation resolvedDragOperation = m_page->dragController().dragUpdated(dragData);
-        send(Messages::WebPageProxy::DidPerformDragControllerAction(resolvedDragOperation, m_page->dragController().mouseIsOverFileInput(), m_page->dragController().numberOfItemsToBeAccepted(), m_page->dragCaretController().caretPosition().absoluteCaretBounds()));
+        send(Messages::WebPageProxy::DidPerformDragControllerAction(resolvedDragOperation, m_page->dragController().mouseIsOverFileInput(), m_page->dragController().numberOfItemsToBeAccepted(), m_page->dragCaretController().caretRectInRootViewCoordinates()));
         break;
     }
     case DragControllerActionExited:
index c56a8159ce0f936126c49585dc2eb43f937b2130..bbf38b9a67902125c7e81b7d17b5027a2961b3f2 100644 (file)
@@ -1,3 +1,26 @@
+2017-06-30  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS DnD] Drag caret rect is incorrectly computed when dropping in editable content in iframes
+        https://bugs.webkit.org/show_bug.cgi?id=174017
+        <rdar://problem/32959782>
+
+        Reviewed by Simon Fraser.
+
+        Add a new test verifying that the drag caret is visually contained within the bounds of an iframe. Before these
+        changes, the caret would appear outside of the iframe.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKit2Cocoa/contenteditable-in-iframe.html: Added.
+        * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
+        (checkDragCaretRectIsContainedInRect):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/ios/DataInteractionSimulator.h:
+        * TestWebKitAPI/ios/DataInteractionSimulator.mm:
+        (-[DataInteractionSimulator _resetSimulatedState]):
+        (-[DataInteractionSimulator _concludeDataInteractionAndPerformOperationIfNecessary]):
+        (-[DataInteractionSimulator _advanceProgress]):
+        (-[DataInteractionSimulator lastKnownDragCaretRect]):
+
 2017-06-30  Jonathan Bedard  <jbedard@apple.com>
 
         Add support for different versions of iOS when loading test expectations
index 69bf47d37299cc297887b0f0b7b702568006296b..e1fb25527e3827ce6c1e90eac1577621d8a7a8ce 100644 (file)
                F47728991E4AE3C1007ABF6A /* full-page-contenteditable.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F47728981E4AE3AD007ABF6A /* full-page-contenteditable.html */; };
                F4856CA31E649EA8009D7EE7 /* attachment-element.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4856CA21E6498A8009D7EE7 /* attachment-element.html */; };
                F4A32EC41F05F3850047C544 /* dragstart-change-selection-offscreen.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */; };
+               F4A32ECB1F0643370047C544 /* contenteditable-in-iframe.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4A32ECA1F0642F40047C544 /* contenteditable-in-iframe.html */; };
                F4B825D81EF4DBFB006E417F /* compressed-files.zip in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4B825D61EF4DBD4006E417F /* compressed-files.zip */; };
                F4BFA68E1E4AD08000154298 /* DragAndDropPasteboardTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4BFA68C1E4AD08000154298 /* DragAndDropPasteboardTests.mm */; };
                F4C2AB221DD6D95E00E06D5B /* enormous-video-with-sound.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4C2AB211DD6D94100E06D5B /* enormous-video-with-sound.html */; };
                        dstSubfolderSpec = 7;
                        files = (
                                F4A32EC41F05F3850047C544 /* dragstart-change-selection-offscreen.html in Copy Resources */,
+                               F4A32ECB1F0643370047C544 /* contenteditable-in-iframe.html in Copy Resources */,
                                F469FB241F01804B00401539 /* contenteditable-and-target.html in Copy Resources */,
                                F4B825D81EF4DBFB006E417F /* compressed-files.zip in Copy Resources */,
                                F41AB99F1EF4696B0083FA08 /* autofocus-contenteditable.html in Copy Resources */,
                F47D30ED1ED28A6C000482E1 /* gif-and-file-input.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "gif-and-file-input.html"; sourceTree = "<group>"; };
                F4856CA21E6498A8009D7EE7 /* attachment-element.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "attachment-element.html"; sourceTree = "<group>"; };
                F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "dragstart-change-selection-offscreen.html"; sourceTree = "<group>"; };
+               F4A32ECA1F0642F40047C544 /* contenteditable-in-iframe.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "contenteditable-in-iframe.html"; sourceTree = "<group>"; };
                F4B825D61EF4DBD4006E417F /* compressed-files.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = "compressed-files.zip"; sourceTree = "<group>"; };
                F4BFA68C1E4AD08000154298 /* DragAndDropPasteboardTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DragAndDropPasteboardTests.mm; sourceTree = "<group>"; };
                F4C2AB211DD6D94100E06D5B /* enormous-video-with-sound.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "enormous-video-with-sound.html"; sourceTree = "<group>"; };
                                F41AB9971EF4692C0083FA08 /* background-image-link-and-input.html */,
                                F469FB231F01803500401539 /* contenteditable-and-target.html */,
                                F41AB99C1EF4692C0083FA08 /* contenteditable-and-textarea.html */,
+                               F4A32ECA1F0642F40047C544 /* contenteditable-in-iframe.html */,
                                F41AB99E1EF4692C0083FA08 /* div-and-large-image.html */,
                                F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */,
                                F41AB99B1EF4692C0083FA08 /* file-uploading.html */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/contenteditable-in-iframe.html b/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/contenteditable-in-iframe.html
new file mode 100644 (file)
index 0000000..1eb785a
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width">
+<style>
+body {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+    position: absolute;
+}
+#container {
+    width: calc(100% - 100px);
+    height: calc(100% - 100px);
+    margin: 50px;
+}
+#editor {
+    width: 100%;
+    height: 100%;
+}
+</style>
+<div id="container"><iframe id="editor" src="data:text/html,<body%20contenteditable%20style='width:%20100%;%20height:%20100%;%20margin:%200;'>"></iframe></div>
+</html>
index 15fa141a3342f0e1ac780c7c0f4de667c63fdd3e..37984f98c3f8e1c160ced0a0d4a2a2b2e50186aa 100644 (file)
@@ -132,6 +132,14 @@ static void checkStringArraysAreEqual(NSArray<NSString *> *expected, NSArray<NSS
     }
 }
 
+static void checkDragCaretRectIsContainedInRect(CGRect caretRect, CGRect containerRect)
+{
+    BOOL contained = CGRectContainsRect(containerRect, caretRect);
+    EXPECT_TRUE(contained);
+    if (!contained)
+        NSLog(@"Expected caret rect: %@ to fit within container rect: %@", NSStringFromCGRect(caretRect), NSStringFromCGRect(containerRect));
+}
+
 namespace TestWebKitAPI {
 
 TEST(DataInteractionTests, ImageToContentEditable)
@@ -434,6 +442,25 @@ TEST(DataInteractionTests, EnterAndLeaveEvents)
     checkSelectionRectsWithLogging(@[ ], [dataInteractionSimulator finalSelectionRects]);
 }
 
+TEST(DataInteractionTests, ExternalSourcePlainTextToIFrame)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"contenteditable-in-iframe"];
+
+    auto itemProvider = adoptNS([[UIItemProvider alloc] init]);
+    [itemProvider registerObject:@"Hello world" visibility:UIItemProviderRepresentationOptionsVisibilityAll];
+
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+    [simulator setExternalItemProviders:@[ itemProvider.get() ]];
+    [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(160, 250)];
+
+    auto containerLeft = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().left"].floatValue;
+    auto containerTop = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().top"].floatValue;
+    auto containerWidth = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().width"].floatValue;
+    auto containerHeight = [webView stringByEvaluatingJavaScript:@"container.getBoundingClientRect().height"].floatValue;
+    checkDragCaretRectIsContainedInRect([simulator lastKnownDragCaretRect], CGRectMake(containerLeft, containerTop, containerWidth, containerHeight));
+}
+
 TEST(DataInteractionTests, ExternalSourceJSONToFileInput)
 {
     RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
index 1808aa00fe57896298df02a450e9115252c599b8..ba01117a5850e13ad3f04950fa9c02c2d8f8260e 100644 (file)
@@ -60,6 +60,7 @@ typedef NS_ENUM(NSInteger, DataInteractionPhase) {
     RetainPtr<NSArray *> _finalSelectionRects;
     CGPoint _startLocation;
     CGPoint _endLocation;
+    CGRect _lastKnownDragCaretRect;
 
     bool _isDoneWaitingForInputSession;
     BOOL _shouldPerformOperation;
@@ -85,6 +86,7 @@ typedef NS_ENUM(NSInteger, DataInteractionPhase) {
 @property (nonatomic, readonly) NSArray *observedEventNames;
 @property (nonatomic, readonly) NSArray *finalSelectionRects;
 @property (nonatomic, readonly) DataInteractionPhase phase;
+@property (nonatomic, readonly) CGRect lastKnownDragCaretRect;
 
 @end
 
index d7040c6d374084a2d272565cf5bb6e6c69f0a882..01a451480a1750949704f5fa313dd14814cd8be8 100644 (file)
@@ -291,6 +291,7 @@ static NSArray *dataInteractionEventNames()
     _dataInteractionSession = nil;
     _dataOperationSession = nil;
     _shouldPerformOperation = NO;
+    _lastKnownDragCaretRect = CGRectZero;
 }
 
 - (NSArray *)observedEventNames
@@ -360,6 +361,7 @@ static NSArray *dataInteractionEventNames()
 
 - (void)_concludeDataInteractionAndPerformOperationIfNecessary
 {
+    _lastKnownDragCaretRect = [_webView _dragCaretRect];
     if (_shouldPerformOperation) {
         [_webView _simulateDataInteractionPerformOperation:_dataOperationSession.get()];
         _phase = DataInteractionPerforming;
@@ -376,6 +378,7 @@ static NSArray *dataInteractionEventNames()
 
 - (void)_advanceProgress
 {
+    _lastKnownDragCaretRect = [_webView _dragCaretRect];
     _currentProgress += progressIncrementStep;
     CGPoint locationInWindow = self._currentLocation;
     [_dataInteractionSession setMockLocationInWindow:locationInWindow];
@@ -468,6 +471,11 @@ static NSArray *dataInteractionEventNames()
     return _phase;
 }
 
+- (CGRect)lastKnownDragCaretRect
+{
+    return _lastKnownDragCaretRect;
+}
+
 - (void)waitForInputSession
 {
     _isDoneWaitingForInputSession = false;