[iOS WK2] Support tapping to add items to the current drag session in web content
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Sep 2017 02:09:28 +0000 (02:09 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Sep 2017 02:09:28 +0000 (02:09 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176421
<rdar://problem/31144674>

Reviewed by Tim Horton.

Source/WebCore:

Refactors some drag initiation logic to handle starting a drag when data has already been written to the
pasteboard (in the case of iOS, WebItemProviderPasteboard). See annotated comments below for more detail.

Tests: DataInteractionTests.AdditionalLinkAndImageIntoContentEditable

* page/DragActions.h:
* page/DragController.cpp:
(WebCore::DragController::startDrag):

Add a HasNonDefaultPasteboardData argument here, and replace checks for !dataTransfer.pasteboard().hasData()
with checks for whether the argument is HasNonDefaultPasteboardData::No. These checks for Pasteboard::hasData()
currently prevent us from overwriting custom pasteboard data, in the case that the page has written pasteboard
data using the event's DataTransfer. However, in the case of adding additional drag items to the session, we
will already have pasteboard data, so these checks will prevent us from writing default data to the pasteboard.
See EventHandler::handleDrag for more detail.

* page/DragController.h:
* page/DragState.h:

Remove the draggedContentRange member from DragState. See below.

* page/EventHandler.cpp:
(WebCore::removeDraggedContentDocumentMarkersFromAllFramesInPage):

Simplify the handling of dragged content range markers. Instead of storing the DOM Range being dragged and
removing/repainting the range after dragging ends, just repaint the contentRenderer of the frame being dragged.
When the dragging session has completely ended, remove all dragged content ranges from the page's mainframe and
all of its subframes, and repaint everything.

(WebCore::EventHandler::dragCancelled):
(WebCore::EventHandler::didStartDrag):
(WebCore::EventHandler::dragSourceEndedAt):

Add a MayExtendDragSession argument, indicating whether or not the web process will attempt to continue the drag
session, in which case EventHandler::dragSourceEndedAt should not remove any existing dragged content range
document markers.

(WebCore::EventHandler::dispatchDragStartEvent):

Helper method to dispatch a `dragstart` event, return whether or not to proceed with the drag, and also compute
(as an outparam) whether or not custom pasteboard data was written during the event.

(WebCore::EventHandler::handleDrag):

If custom data was written during `dragstart`, pass along HasNonDefaultPasteboardData::Yes when calling
DragController::startDrag.

(WebCore::repaintContentsOfRange): Deleted.
* page/EventHandler.h:
* page/ios/EventHandlerIOS.mm:
(WebCore::EventHandler::tryToBeginDataInteractionAtPoint):
* platform/Pasteboard.h:
* platform/ios/PasteboardIOS.mm:
(WebCore::Pasteboard::changeCount const):
* platform/ios/WebItemProviderPasteboard.mm:
(-[WebItemProviderPasteboard setItemProviders:]):

Stop clearing out the staged item provider registration list when setting item providers. After refactoring in
r221595, staged registration lists are now automatically cleared out when (1) the drag-and-drop interaction
state is cleared out in the UI process, or (2) when the registration list is taken by WKContentView (see
-takeRegistrationList) when generating an item provider.

* platform/mac/PasteboardMac.mm:
(WebCore::Pasteboard::changeCount const):

Add a changeCount method to Pasteboard on Cocoa platforms (Mac, iOS) which support changeCount natively. In
theory, there's no reason Windows, GTK and WPE ports can't also implement a similar mechanism in
PlatformPasteboard, but this isn't needed for anything yet. Upon dragstart, it is safe to assume that the
pasteboard has been cleared on these platforms, so checking for Pasteboard::hasData (as we do for all platforms
currently) is sufficient.

Source/WebKit:

To request additional drag items, end the current drag in the web page and try to begin a drag at the new
location. This process is transparent to the UI process, which still maintains the same UIDragSession with the
old drag source.

As opposed to firing a new event (for instance: `adddragitem`), this approach is taken to ensure that if the
page wants to preventDefault() on `dragstart`, it would also prevent the user from adding it as an additional
drag item. Using the new event approach, dealing with this case would either require the page to listen for a
new event and call preventDefault(), which would break compatibility with pages that only preventDefault() on
`dragstart`, or it would require the default behavior of this new event to be _not_ adding a drag item, in which
case this approach would require pages to adopt the new event in some form.

* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::requestAdditionalItemsForDragSession):

Tools:

Adds a new drag and drop test that begins a drag on a text selection, adds an image and a link, and then drops
into a contenteditable area. This verifies that the text, link and image are inserted into the editable area
upon drop.

* TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
(TestWebKitAPI::TEST):

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

16 files changed:
Source/WebCore/ChangeLog
Source/WebCore/page/DragActions.h
Source/WebCore/page/DragController.cpp
Source/WebCore/page/DragController.h
Source/WebCore/page/DragState.h
Source/WebCore/page/EventHandler.cpp
Source/WebCore/page/EventHandler.h
Source/WebCore/page/ios/EventHandlerIOS.mm
Source/WebCore/platform/Pasteboard.h
Source/WebCore/platform/ios/PasteboardIOS.mm
Source/WebCore/platform/ios/WebItemProviderPasteboard.mm
Source/WebCore/platform/mac/PasteboardMac.mm
Source/WebKit/ChangeLog
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/ios/DataInteractionTests.mm

index 1ef00e3296426ce48e2aa2872eff76f880d0743c..258863a25fb544ff7049b9129e59361f81143778 100644 (file)
@@ -1,3 +1,82 @@
+2017-09-11  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS WK2] Support tapping to add items to the current drag session in web content
+        https://bugs.webkit.org/show_bug.cgi?id=176421
+        <rdar://problem/31144674>
+
+        Reviewed by Tim Horton.
+
+        Refactors some drag initiation logic to handle starting a drag when data has already been written to the
+        pasteboard (in the case of iOS, WebItemProviderPasteboard). See annotated comments below for more detail.
+
+        Tests: DataInteractionTests.AdditionalLinkAndImageIntoContentEditable
+
+        * page/DragActions.h:
+        * page/DragController.cpp:
+        (WebCore::DragController::startDrag):
+
+        Add a HasNonDefaultPasteboardData argument here, and replace checks for !dataTransfer.pasteboard().hasData()
+        with checks for whether the argument is HasNonDefaultPasteboardData::No. These checks for Pasteboard::hasData()
+        currently prevent us from overwriting custom pasteboard data, in the case that the page has written pasteboard
+        data using the event's DataTransfer. However, in the case of adding additional drag items to the session, we
+        will already have pasteboard data, so these checks will prevent us from writing default data to the pasteboard.
+        See EventHandler::handleDrag for more detail.
+
+        * page/DragController.h:
+        * page/DragState.h:
+
+        Remove the draggedContentRange member from DragState. See below.
+
+        * page/EventHandler.cpp:
+        (WebCore::removeDraggedContentDocumentMarkersFromAllFramesInPage):
+
+        Simplify the handling of dragged content range markers. Instead of storing the DOM Range being dragged and
+        removing/repainting the range after dragging ends, just repaint the contentRenderer of the frame being dragged.
+        When the dragging session has completely ended, remove all dragged content ranges from the page's mainframe and
+        all of its subframes, and repaint everything.
+
+        (WebCore::EventHandler::dragCancelled):
+        (WebCore::EventHandler::didStartDrag):
+        (WebCore::EventHandler::dragSourceEndedAt):
+
+        Add a MayExtendDragSession argument, indicating whether or not the web process will attempt to continue the drag
+        session, in which case EventHandler::dragSourceEndedAt should not remove any existing dragged content range
+        document markers.
+
+        (WebCore::EventHandler::dispatchDragStartEvent):
+
+        Helper method to dispatch a `dragstart` event, return whether or not to proceed with the drag, and also compute
+        (as an outparam) whether or not custom pasteboard data was written during the event.
+
+        (WebCore::EventHandler::handleDrag):
+
+        If custom data was written during `dragstart`, pass along HasNonDefaultPasteboardData::Yes when calling
+        DragController::startDrag.
+
+        (WebCore::repaintContentsOfRange): Deleted.
+        * page/EventHandler.h:
+        * page/ios/EventHandlerIOS.mm:
+        (WebCore::EventHandler::tryToBeginDataInteractionAtPoint):
+        * platform/Pasteboard.h:
+        * platform/ios/PasteboardIOS.mm:
+        (WebCore::Pasteboard::changeCount const):
+        * platform/ios/WebItemProviderPasteboard.mm:
+        (-[WebItemProviderPasteboard setItemProviders:]):
+
+        Stop clearing out the staged item provider registration list when setting item providers. After refactoring in
+        r221595, staged registration lists are now automatically cleared out when (1) the drag-and-drop interaction
+        state is cleared out in the UI process, or (2) when the registration list is taken by WKContentView (see
+        -takeRegistrationList) when generating an item provider.
+
+        * platform/mac/PasteboardMac.mm:
+        (WebCore::Pasteboard::changeCount const):
+
+        Add a changeCount method to Pasteboard on Cocoa platforms (Mac, iOS) which support changeCount natively. In
+        theory, there's no reason Windows, GTK and WPE ports can't also implement a similar mechanism in
+        PlatformPasteboard, but this isn't needed for anything yet. Upon dragstart, it is safe to assume that the
+        pasteboard has been cleared on these platforms, so checking for Pasteboard::hasData (as we do for all platforms
+        currently) is sufficient.
+
 2017-09-11  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r221762.
index ad28634722556dd5da8b499aab1acb6e290487c1..2c1a37a6e0be5273c2c550e4849f26fe3525875e 100644 (file)
@@ -62,5 +62,8 @@ namespace WebCore {
         DragOperationDelete  = 32,
         DragOperationEvery   = UINT_MAX
     } DragOperation;
+
+    enum class MayExtendDragSession { No, Yes };
+    enum class HasNonDefaultPasteboardData { No, Yes };
     
 } // namespace WebCore
index 4713c391033a259d4195e1b511080b6359299f2e..a1d1e574acd33da772a4300d5db4bdb908017aa6 100644 (file)
@@ -868,7 +868,7 @@ static IntPoint dragLocForSelectionDrag(Frame& src)
     return IntPoint(xpos, ypos);
 }
 
-bool DragController::startDrag(Frame& src, const DragState& state, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin)
+bool DragController::startDrag(Frame& src, const DragState& state, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, HasNonDefaultPasteboardData hasData)
 {
     if (!src.view() || !src.contentRenderer() || !state.source)
         return false;
@@ -933,14 +933,14 @@ bool DragController::startDrag(Frame& src, const DragState& state, DragOperation
     ASSERT(state.source);
     Element& element = *state.source;
 
-    bool mustUseLegacyDragClient = dataTransfer.pasteboard().hasData() || m_client.useLegacyDragClient();
+    bool mustUseLegacyDragClient = hasData == HasNonDefaultPasteboardData::Yes || m_client.useLegacyDragClient();
 
     IntRect dragImageBounds;
     Image* image = getImage(element);
     if (state.type == DragSourceActionSelection) {
         PasteboardWriterData pasteboardWriterData;
 
-        if (!dataTransfer.pasteboard().hasData()) {
+        if (hasData == HasNonDefaultPasteboardData::No) {
             if (src.selection().selection().isNone()) {
                 // The page may have cleared out the selection in the dragstart handler, in which case we should bail
                 // out of the drag, since there is no content to write to the pasteboard.
@@ -1016,7 +1016,7 @@ bool DragController::startDrag(Frame& src, const DragState& state, DragOperation
         // We shouldn't be starting a drag for an image that can't provide an extension.
         // This is an early detection for problems encountered later upon drop.
         ASSERT(!image->filenameExtension().isEmpty());
-        if (!dataTransfer.pasteboard().hasData()) {
+        if (hasData == HasNonDefaultPasteboardData::No) {
             m_draggingImageURL = imageURL;
             if (element.isContentRichlyEditable())
                 selectElement(element);
@@ -1040,7 +1040,7 @@ bool DragController::startDrag(Frame& src, const DragState& state, DragOperation
 
         String textContentWithSimplifiedWhiteSpace = hitTestResult.textContent().simplifyWhiteSpace();
 
-        if (!dataTransfer.pasteboard().hasData()) {
+        if (hasData == HasNonDefaultPasteboardData::No) {
             // Simplify whitespace so the title put on the dataTransfer resembles what the user sees
             // on the web page. This includes replacing newlines with spaces.
             if (mustUseLegacyDragClient)
@@ -1103,7 +1103,7 @@ bool DragController::startDrag(Frame& src, const DragState& state, DragOperation
 
         src.editor().setIgnoreSelectionChanges(true);
         auto previousSelection = src.selection().selection();
-        if (!dataTransfer.pasteboard().hasData()) {
+        if (hasData == HasNonDefaultPasteboardData::No) {
             selectElement(element);
             if (!attachmentURL.isEmpty()) {
                 // Use the attachment URL specified by the file attribute to populate the pasteboard.
index fb901f3f1d5c2679f0460e7e786071216742f7e0..05b8487d1d64f5521747a045b634c50af01e7dd0 100644 (file)
@@ -91,7 +91,7 @@ struct DragState;
         
         WEBCORE_EXPORT void placeDragCaret(const IntPoint&);
         
-        bool startDrag(Frame& src, const DragState&, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin);
+        bool startDrag(Frame& src, const DragState&, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, HasNonDefaultPasteboardData);
         static const IntSize& maxDragImageSize();
         
         static const int MaxOriginalImageArea;
index 6258abd5ccd544f0f7952c445013a58f8f21c157..6d1625900e345f33efa6508a05e5336214ac8bbd 100644 (file)
@@ -34,7 +34,6 @@ namespace WebCore {
 
 struct DragState {
     RefPtr<Element> source; // Element that may be a drag source, for the current mouse gesture.
-    RefPtr<Range> draggedContentRange;
     bool shouldDispatchEvents;
     DragSourceAction type;
     RefPtr<DataTransfer> dataTransfer; // Used on only the source side of dragging.
index e538ba6c5312d9c819eb5a756640ac5aba8cd3ab..80b1c66654398a90ace466a6f4c7e6b962aa2ac7 100644 (file)
@@ -66,6 +66,7 @@
 #include "MouseEventWithHitTestResults.h"
 #include "Page.h"
 #include "PageOverlayController.h"
+#include "Pasteboard.h"
 #include "PlatformEvent.h"
 #include "PlatformKeyboardEvent.h"
 #include "PlatformWheelEvent.h"
@@ -3465,31 +3466,22 @@ void EventHandler::invalidateDataTransfer()
     dragState().dataTransfer = nullptr;
 }
 
-static void repaintContentsOfRange(RefPtr<Range> range)
+static void removeDraggedContentDocumentMarkersFromAllFramesInPage(Page& page)
 {
-    if (!range)
-        return;
-
-    auto* container = range->commonAncestorContainer();
-    if (!container)
-        return;
-
-    // This ensures that all nodes enclosed in this Range are repainted.
-    if (auto rendererToRepaint = container->renderer()) {
-        if (auto* containingRenderer = rendererToRepaint->container())
-            rendererToRepaint = containingRenderer;
-        rendererToRepaint->repaint();
+    for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        if (auto* document = frame->document())
+            document->markers().removeMarkers(DocumentMarker::DraggedContent);
     }
+
+    if (auto* mainFrameRenderer = page.mainFrame().contentRenderer())
+        mainFrameRenderer->repaintRootContents();
 }
 
 void EventHandler::dragCancelled()
 {
 #if ENABLE(DATA_INTERACTION)
-    if (auto range = dragState().draggedContentRange) {
-        range->ownerDocument().markers().removeMarkers(DocumentMarker::DraggedContent);
-        repaintContentsOfRange(range);
-    }
-    dragState().draggedContentRange = nullptr;
+    if (auto* page = m_frame.page())
+        removeDraggedContentDocumentMarkersFromAllFramesInPage(*page);
 #endif
 }
 
@@ -3504,22 +3496,24 @@ void EventHandler::didStartDrag()
     if (!renderer)
         return;
 
+    RefPtr<Range> draggedContentRange;
     if (dragState().type & DragSourceActionSelection)
-        dragState().draggedContentRange = m_frame.selection().selection().toNormalizedRange();
+        draggedContentRange = m_frame.selection().selection().toNormalizedRange();
     else {
         Position startPosition(dragSource.get(), Position::PositionIsBeforeAnchor);
         Position endPosition(dragSource.get(), Position::PositionIsAfterAnchor);
-        dragState().draggedContentRange = Range::create(dragSource->document(), startPosition, endPosition);
+        draggedContentRange = Range::create(dragSource->document(), startPosition, endPosition);
     }
 
-    if (auto range = dragState().draggedContentRange) {
-        range->ownerDocument().markers().addDraggedContentMarker(range.get());
-        repaintContentsOfRange(range);
+    if (draggedContentRange) {
+        draggedContentRange->ownerDocument().markers().addDraggedContentMarker(draggedContentRange.get());
+        if (auto* renderer = m_frame.contentRenderer())
+            renderer->repaintRootContents();
     }
 #endif
 }
 
-void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation)
+void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation, MayExtendDragSession mayExtendDragSession)
 {
     // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses.
     HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent);
@@ -3532,9 +3526,9 @@ void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperat
     }
     invalidateDataTransfer();
 
-    if (auto range = dragState().draggedContentRange) {
-        range->ownerDocument().markers().removeMarkers(DocumentMarker::DraggedContent);
-        repaintContentsOfRange(range);
+    if (mayExtendDragSession == MayExtendDragSession::No) {
+        if (auto* page = m_frame.page())
+            removeDraggedContentDocumentMarkersFromAllFramesInPage(*page);
     }
 
     dragState().source = nullptr;
@@ -3555,6 +3549,23 @@ bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const Pla
 {
     return !dispatchDragEvent(eventType, *dragState().source, event, dragState().dataTransfer.get());
 }
+
+bool EventHandler::dispatchDragStartEvent(HasNonDefaultPasteboardData& hasNonDefaultPasteboardData)
+{
+#if PLATFORM(COCOA)
+    auto changeCountBeforeDragStart = dragState().dataTransfer->pasteboard().changeCount();
+#endif
+
+    bool mayStartDrag = dispatchDragSrcEvent(eventNames().dragstartEvent, m_mouseDown);
+
+#if PLATFORM(COCOA)
+    hasNonDefaultPasteboardData = changeCountBeforeDragStart != dragState().dataTransfer->pasteboard().changeCount() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
+#else
+    hasNonDefaultPasteboardData = dragState().dataTransfer->pasteboard().hasData() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No;
+#endif
+
+    return mayStartDrag && !m_frame.selection().selection().isInPasswordField();
+}
     
 static bool ExactlyOneBitSet(DragSourceAction n)
 {
@@ -3660,6 +3671,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr
     invalidateDataTransfer();
 
     dragState().dataTransfer = createDraggingDataTransfer();
+    HasNonDefaultPasteboardData hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No;
     
     if (dragState().shouldDispatchEvents) {
         // Check to see if the is a DOM based drag. If it is, get the DOM specified drag image and offset.
@@ -3680,8 +3692,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr
             }
         } 
 
-        m_mouseDownMayStartDrag = dispatchDragSrcEvent(eventNames().dragstartEvent, m_mouseDown)
-            && !m_frame.selection().selection().isInPasswordField();
+        m_mouseDownMayStartDrag = dispatchDragStartEvent(hasNonDefaultPasteboardData);
 
         dragState().dataTransfer->makeInvalidForSecurity();
 
@@ -3698,7 +3709,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr
     
     if (m_mouseDownMayStartDrag) {
         Page* page = m_frame.page();
-        m_didStartDrag = page && page->dragController().startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos);
+        m_didStartDrag = page && page->dragController().startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos, hasNonDefaultPasteboardData);
         // In WebKit2 we could re-enter this code and start another drag.
         // On OS X this causes problems with the ownership of the pasteboard and the promised types.
         if (m_didStartDrag) {
index 2251f9aed540bde4a7930b07c2cd13df62c24606..6663c8f2a89d3f22c3bef62b097c46aedbf67663 100644 (file)
@@ -256,7 +256,7 @@ public:
     
     WEBCORE_EXPORT void didStartDrag();
     WEBCORE_EXPORT void dragCancelled();
-    WEBCORE_EXPORT void dragSourceEndedAt(const PlatformMouseEvent&, DragOperation);
+    WEBCORE_EXPORT void dragSourceEndedAt(const PlatformMouseEvent&, DragOperation, MayExtendDragSession = MayExtendDragSession::No);
 #endif
 
     void focusDocumentView();
@@ -405,6 +405,7 @@ private:
     void clearDragState();
 
     bool dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent&);
+    bool dispatchDragStartEvent(HasNonDefaultPasteboardData&);
 
     bool dragHysteresisExceeded(const FloatPoint&) const;
     bool dragHysteresisExceeded(const IntPoint&) const;
index 80644b7fd823dbbf205d292b00b36ccdfd8427ec..8f0fb4e2eaba3dd96ceeb4438f8f3b039958b142 100644 (file)
@@ -606,7 +606,6 @@ bool EventHandler::tryToBeginDataInteractionAtPoint(const IntPoint& clientPositi
 
     SetForScope<bool> mousePressed(m_mousePressed, true);
     dragState().source = nullptr;
-    dragState().draggedContentRange = nullptr;
     m_mouseDownPos = protectedFrame->view()->windowToContents(syntheticMouseMoveEvent.position());
 
     return handleMouseDraggedEvent(hitTestedMouseEvent, DontCheckDragHysteresis);
index 5e26bf2fbf66472034f89ca9caad43cb8fffc539..6ee0876f0b8218c1243b0c2811ec61577f9e6ef9 100644 (file)
@@ -219,6 +219,7 @@ public:
 
     WEBCORE_EXPORT static NSArray *supportedFileUploadPasteboardTypes();
     const String& name() const { return m_pasteboardName; }
+    long changeCount() const;
 #endif
 
 #if PLATFORM(WIN)
index 731782ccc862783d4f2d921ebdc59b16a6bd921b..31b0482360a90be2aad640e6a180d59e6f77e393 100644 (file)
@@ -299,6 +299,11 @@ void Pasteboard::readRespectingUTIFidelities(PasteboardWebContentReader& reader)
     }
 }
 
+long Pasteboard::changeCount() const
+{
+    return platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName);
+}
+
 NSArray *Pasteboard::supportedWebContentPasteboardTypes()
 {
     return @[(id)WebArchivePboardType, (id)kUTTypeFlatRTFD, (id)kUTTypeRTF, (id)kUTTypeHTML, (id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF, (id)kUTTypeURL, (id)kUTTypeText];
index 8296559ea3d1acd5b21d9877b4053d992841a9e8..1af13d46dfbbf2d9f4b87c0752f399e8720f7d26 100644 (file)
@@ -277,7 +277,6 @@ typedef NSDictionary<NSString *, NSURL *> TypeToFileURLMap;
     _itemProviders = itemProviders;
     _changeCount++;
     _cachedTypeIdentifiers = nil;
-    _stagedRegistrationInfoList = nil;
 
     NSMutableArray *typeToFileURLMaps = [NSMutableArray arrayWithCapacity:itemProviders.count];
     [itemProviders enumerateObjectsUsingBlock:[typeToFileURLMaps] (UIItemProvider *, NSUInteger, BOOL *) {
index cd62a2dcbbc1198856524709bed8dffed2d53f28..a88c5d98b44fb933ff0a6e3514676f0da18c8c83 100644 (file)
@@ -107,6 +107,11 @@ static Vector<String> writableTypesForImage()
     return types;
 }
 
+long Pasteboard::changeCount() const
+{
+    return platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName);
+}
+
 NSArray *Pasteboard::supportedFileUploadPasteboardTypes()
 {
     return @[ (NSString *)NSFilesPromisePboardType, (NSString *)NSFilenamesPboardType ];
index a236fd4d38da9763d4e0ea3ae893721af3cf4d59..b8a22a7ba57a4309b337675b99a557aea547c2cc 100644 (file)
@@ -1,3 +1,25 @@
+2017-09-11  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS WK2] Support tapping to add items to the current drag session in web content
+        https://bugs.webkit.org/show_bug.cgi?id=176421
+        <rdar://problem/31144674>
+
+        Reviewed by Tim Horton.
+
+        To request additional drag items, end the current drag in the web page and try to begin a drag at the new
+        location. This process is transparent to the UI process, which still maintains the same UIDragSession with the
+        old drag source.
+
+        As opposed to firing a new event (for instance: `adddragitem`), this approach is taken to ensure that if the
+        page wants to preventDefault() on `dragstart`, it would also prevent the user from adding it as an additional
+        drag item. Using the new event approach, dealing with this case would either require the page to listen for a
+        new event and call preventDefault(), which would break compatibility with pages that only preventDefault() on
+        `dragstart`, or it would require the default behavior of this new event to be _not_ adding a drag item, in which
+        case this approach would require pages to adopt the new event in some form.
+
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::requestAdditionalItemsForDragSession):
+
 2017-09-11  Alex Christensen  <achristensen@webkit.org>
 
         Clean up API::UIClient
index 477916119bfd21e8a00daef01c4e139252d677d2..bcdb0bf06a0ca9c39ff5f3c5b4a52545612bc051 100644 (file)
@@ -57,6 +57,7 @@
 #import <WebCore/DataDetection.h>
 #import <WebCore/DiagnosticLoggingClient.h>
 #import <WebCore/DiagnosticLoggingKeys.h>
+#import <WebCore/DragController.h>
 #import <WebCore/Editing.h>
 #import <WebCore/Editor.h>
 #import <WebCore/Element.h>
@@ -633,9 +634,15 @@ void WebPage::requestStartDataInteraction(const IntPoint& clientPosition, const
 
 void WebPage::requestAdditionalItemsForDragSession(const IntPoint& clientPosition, const IntPoint& globalPosition)
 {
-    notImplemented();
+    // To augment the platform drag session with additional items, end the current drag session and begin a new drag session with the new drag item.
+    // This process is opaque to the UI process, which still maintains the old drag item in its drag session. Similarly, this persistent drag session
+    // is opaque to the web process, which only sees that the current drag has ended, and that a new one is beginning.
+    PlatformMouseEvent event(clientPosition, globalPosition, LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, currentTime(), 0, NoTap);
+    m_page->dragController().dragEnded();
+    m_page->mainFrame().eventHandler().dragSourceEndedAt(event, DragOperationNone, MayExtendDragSession::Yes);
 
-    send(Messages::WebPageProxy::DidHandleAdditionalDragItemsRequest(false));
+    bool didHandleDrag = m_page->mainFrame().eventHandler().tryToBeginDataInteractionAtPoint(clientPosition, globalPosition);
+    send(Messages::WebPageProxy::DidHandleAdditionalDragItemsRequest(didHandleDrag));
 }
 
 void WebPage::didConcludeEditDataInteraction()
index c0ab64f338f94e685fccd2cdea4eac87059efbb0..5b5c1d9bf9b4f0865431faa89d2198550bff1d29 100644 (file)
@@ -1,3 +1,18 @@
+2017-09-11  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS WK2] Support tapping to add items to the current drag session in web content
+        https://bugs.webkit.org/show_bug.cgi?id=176421
+        <rdar://problem/31144674>
+
+        Reviewed by Tim Horton.
+
+        Adds a new drag and drop test that begins a drag on a text selection, adds an image and a link, and then drops
+        into a contenteditable area. This verifies that the text, link and image are inserted into the editable area
+        upon drop.
+
+        * TestWebKitAPI/Tests/ios/DataInteractionTests.mm:
+        (TestWebKitAPI::TEST):
+
 2017-09-11  Lucas Forschler  <lforschler@apple.com>
 
         bisect-builds: add --list option
index 38384e09d8ae05c7bb58b2c738b1eb9b18c55c16..5d55c01454871206ab0971c1da629cfd0135583e 100644 (file)
@@ -1394,6 +1394,21 @@ TEST(DataInteractionTests, AdditionalItemsCanBePreventedOnDragStart)
     EXPECT_WK_STREQ("ABCD", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
 }
 
+TEST(DataInteractionTests, AdditionalLinkAndImageIntoContentEditable)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadTestPageNamed:@"selected-text-image-link-and-editable"];
+
+    auto simulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
+    [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 400) additionalItemRequestLocations:@{
+        @0.33: [NSValue valueWithCGPoint:CGPointMake(50, 150)],
+        @0.66: [NSValue valueWithCGPoint:CGPointMake(50, 250)]
+    }];
+    EXPECT_WK_STREQ("ABCD A link", [webView stringByEvaluatingJavaScript:@"editor.textContent"]);
+    EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!editor.querySelector('img')"]);
+    EXPECT_WK_STREQ("https://www.apple.com/", [webView stringByEvaluatingJavaScript:@"editor.querySelector('a').href"]);
+}
+
 } // namespace TestWebKitAPI
 
 #endif // ENABLE(DATA_INTERACTION)