Unreviewed, fix iOS build with recent SDKs.
[WebKit-https.git] / Tools / TestWebKitAPI / ios / DragAndDropSimulatorIOS.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "DragAndDropSimulator.h"
28
29 #if ENABLE(DRAG_SUPPORT) && PLATFORM(IOS_FAMILY)
30
31 #import "InstanceMethodSwizzler.h"
32 #import "PlatformUtilities.h"
33 #import "UIKitSPI.h"
34
35 #import <UIKit/UIDragInteraction.h>
36 #import <UIKit/UIDragItem.h>
37 #import <UIKit/UIInteraction.h>
38 #import <WebKit/WKWebViewPrivate.h>
39 #import <WebKit/_WKFocusedElementInfo.h>
40 #import <WebKit/_WKFormInputSession.h>
41 #import <wtf/RetainPtr.h>
42 #import <wtf/SoftLinking.h>
43
44 SOFT_LINK_FRAMEWORK(UIKit)
45 SOFT_LINK(UIKit, UIApplicationInstantiateSingleton, void, (Class singletonClass), (singletonClass))
46
47 using namespace TestWebKitAPI;
48
49 @implementation WKWebView (DragAndDropTesting)
50
51 - (UIView *)_dragDropInteractionView
52 {
53     return [self valueForKey:@"_currentContentView"];
54 }
55
56 - (id <UIDropInteractionDelegate>)dropInteractionDelegate
57 {
58     return (id <UIDropInteractionDelegate>)self._dragDropInteractionView;
59 }
60
61 - (id <UIDragInteractionDelegate>)dragInteractionDelegate
62 {
63     return (id <UIDragInteractionDelegate>)self._dragDropInteractionView;
64 }
65
66 - (UIDropInteraction *)dropInteraction
67 {
68     UIView *interactionView = self._dragDropInteractionView;
69     for (id <UIInteraction> interaction in interactionView.interactions) {
70         if ([interaction isKindOfClass:[UIDropInteraction class]])
71             return (UIDropInteraction *)interaction;
72     }
73     return nil;
74 }
75
76 - (UIDragInteraction *)dragInteraction
77 {
78     UIView *interactionView = self._dragDropInteractionView;
79     for (id <UIInteraction> interaction in interactionView.interactions) {
80         if ([interaction isKindOfClass:[UIDragInteraction class]])
81             return (UIDragInteraction *)interaction;
82     }
83     return nil;
84 }
85
86 @end
87
88 @implementation MockDragDropSession
89
90 - (instancetype)initWithItems:(NSArray <UIDragItem *>*)items location:(CGPoint)locationInWindow window:(UIWindow *)window allowMove:(BOOL)allowMove
91 {
92     if (self = [super init]) {
93         _mockItems = items;
94         _mockLocationInWindow = locationInWindow;
95         _window = window;
96         _allowMove = allowMove;
97     }
98     return self;
99 }
100
101 - (BOOL)allowsMoveOperation
102 {
103     return _allowMove;
104 }
105
106 - (BOOL)isRestrictedToDraggingApplication
107 {
108     return NO;
109 }
110
111 - (BOOL)hasItemsConformingToTypeIdentifiers:(NSArray<NSString *> *)typeIdentifiers
112 {
113     for (NSString *typeIdentifier in typeIdentifiers) {
114         BOOL hasItemConformingToType = NO;
115         for (UIDragItem *item in self.items)
116             hasItemConformingToType |= [[item.itemProvider registeredTypeIdentifiers] containsObject:typeIdentifier];
117         if (!hasItemConformingToType)
118             return NO;
119     }
120     return YES;
121 }
122
123 - (BOOL)canLoadObjectsOfClass:(Class<NSItemProviderReading>)aClass
124 {
125     for (UIDragItem *item in self.items) {
126         if ([item.itemProvider canLoadObjectOfClass:aClass])
127             return YES;
128     }
129     return NO;
130 }
131
132 - (BOOL)canLoadObjectsOfClasses:(NSArray<Class<NSItemProviderReading>> *)classes
133 {
134     for (Class<NSItemProviderReading> aClass in classes) {
135         BOOL canLoad = NO;
136         for (UIDragItem *item in self.items)
137             canLoad |= [item.itemProvider canLoadObjectOfClass:aClass];
138         if (!canLoad)
139             return NO;
140     }
141     return YES;
142 }
143
144 - (NSArray<UIDragItem *> *)items
145 {
146     return _mockItems.get();
147 }
148
149 - (void)setItems:(NSArray<UIDragItem *> *)items
150 {
151     _mockItems = items;
152 }
153
154 - (void)addItems:(NSArray<UIDragItem *> *)items
155 {
156     if (![items count])
157         return;
158
159     if (![_mockItems count])
160         _mockItems = items;
161     else
162         _mockItems = [_mockItems arrayByAddingObjectsFromArray:items];
163 }
164
165 - (CGPoint)locationInView:(UIView *)view
166 {
167     return [_window convertPoint:_mockLocationInWindow toView:view];
168 }
169
170 @end
171
172 @implementation MockDropSession
173
174 - (instancetype)initWithProviders:(NSArray<NSItemProvider *> *)providers location:(CGPoint)locationInWindow window:(UIWindow *)window allowMove:(BOOL)allowMove
175 {
176     auto items = adoptNS([[NSMutableArray alloc] init]);
177     for (NSItemProvider *itemProvider in providers)
178         [items addObject:[[[UIDragItem alloc] initWithItemProvider:itemProvider] autorelease]];
179
180     return [super initWithItems:items.get() location:locationInWindow window:window allowMove:allowMove];
181 }
182
183 - (BOOL)isLocal
184 {
185     return YES;
186 }
187
188 - (NSProgress *)progress
189 {
190     return [NSProgress discreteProgressWithTotalUnitCount:100];
191 }
192
193 - (void)setProgressIndicatorStyle:(UIDropSessionProgressIndicatorStyle)progressIndicatorStyle
194 {
195 }
196
197 - (UIDropSessionProgressIndicatorStyle)progressIndicatorStyle
198 {
199     return UIDropSessionProgressIndicatorStyleNone;
200 }
201
202 - (NSUInteger)operationMask
203 {
204     return 0;
205 }
206
207 - (id <UIDragSession>)localDragSession
208 {
209     return nil;
210 }
211
212 - (BOOL)hasItemsConformingToTypeIdentifier:(NSString *)typeIdentifier
213 {
214     ASSERT_NOT_REACHED();
215     return NO;
216 }
217
218 - (BOOL)canCreateItemsOfClass:(Class<NSItemProviderReading>)aClass
219 {
220     ASSERT_NOT_REACHED();
221     return NO;
222 }
223
224 - (NSProgress *)loadObjectsOfClass:(Class<NSItemProviderReading>)aClass completion:(void(^)(NSArray<__kindof id <NSItemProviderReading>> *objects))completion
225 {
226     ASSERT_NOT_REACHED();
227     return nil;
228 }
229
230 @end
231
232 @implementation MockDragSession {
233     RetainPtr<id> _localContext;
234 }
235
236 - (instancetype)initWithWindow:(UIWindow *)window allowMove:(BOOL)allowMove
237 {
238     return [super initWithItems:@[ ] location:CGPointZero window:window allowMove:allowMove];
239 }
240
241 - (NSUInteger)localOperationMask
242 {
243     ASSERT_NOT_REACHED();
244     return 0;
245 }
246
247 - (NSUInteger)externalOperationMask
248 {
249     ASSERT_NOT_REACHED();
250     return 0;
251 }
252
253 - (id)session
254 {
255     return nil;
256 }
257
258 - (id)localContext
259 {
260     return _localContext.get();
261 }
262
263 - (void)setLocalContext:(id)localContext
264 {
265     _localContext = localContext;
266 }
267
268 @end
269
270 static double progressIncrementStep = 0.033;
271 static double progressTimeStep = 0.016;
272 static NSString *TestWebKitAPISimulateCancelAllTouchesNotificationName = @"TestWebKitAPISimulateCancelAllTouchesNotificationName";
273
274 static NSArray *dragAndDropEventNames()
275 {
276     static NSArray *eventNames = nil;
277     static dispatch_once_t onceToken;
278     dispatch_once(&onceToken, ^() {
279         eventNames = @[ @"dragenter", @"dragover", @"drop", @"dragleave", @"dragstart" ];
280     });
281     return eventNames;
282 }
283
284 @interface DragAndDropSimulatorApplication : UIApplication
285 @end
286
287 @implementation DragAndDropSimulatorApplication
288
289 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
290 - (void)_cancelAllTouches
291 {
292     [[NSNotificationCenter defaultCenter] postNotificationName:TestWebKitAPISimulateCancelAllTouchesNotificationName object:nil];
293 }
294 IGNORE_WARNINGS_END
295
296 @end
297
298 @implementation DragAndDropSimulator {
299     RetainPtr<TestWKWebView> _webView;
300     RetainPtr<MockDragSession> _dragSession;
301     RetainPtr<MockDropSession> _dropSession;
302     RetainPtr<NSMutableArray> _observedEventNames;
303     RetainPtr<NSArray> _externalItemProviders;
304     RetainPtr<NSArray> _sourceItemProviders;
305     RetainPtr<NSArray> _finalSelectionRects;
306     CGPoint _startLocation;
307     CGPoint _endLocation;
308     CGRect _lastKnownDragCaretRect;
309
310     RetainPtr<NSMutableDictionary<NSNumber *, NSValue *>>_remainingAdditionalItemRequestLocationsByProgress;
311     RetainPtr<NSMutableArray<NSValue *>>_queuedAdditionalItemRequestLocations;
312     RetainPtr<NSMutableArray<UITargetedDragPreview *>> _liftPreviews;
313
314     RetainPtr<NSMutableArray<_WKAttachment *>> _insertedAttachments;
315     RetainPtr<NSMutableArray<_WKAttachment *>> _removedAttachments;
316
317     bool _hasStartedInputSession;
318     double _currentProgress;
319     bool _isDoneWithCurrentRun;
320     DragAndDropPhase _phase;
321
322     BOOL _suppressedSelectionCommandsDuringDrop;
323     RetainPtr<UIDropProposal> _lastKnownDropProposal;
324
325     BlockPtr<BOOL(_WKActivatedElementInfo *)> _showCustomActionSheetBlock;
326     BlockPtr<NSArray *(NSItemProvider *, NSArray *, NSDictionary *)> _convertItemProvidersBlock;
327     BlockPtr<NSArray *(id <UIDropSession>)> _overridePerformDropBlock;
328     BlockPtr<UIDropOperation(UIDropOperation, id)> _overrideDragUpdateBlock;
329     BlockPtr<void(BOOL, NSArray *)> _dropCompletionBlock;
330 }
331
332 - (instancetype)initWithWebViewFrame:(CGRect)frame
333 {
334     return [self initWithWebViewFrame:frame configuration:nil];
335 }
336
337 - (instancetype)initWithWebViewFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
338 {
339     if (configuration)
340         return [self initWithWebView:[[[TestWKWebView alloc] initWithFrame:frame configuration:configuration] autorelease]];
341
342     return [self initWithWebView:[[[TestWKWebView alloc] initWithFrame:frame] autorelease]];
343 }
344
345 - (instancetype)initWithWebView:(TestWKWebView *)webView
346 {
347     if (self = [super init]) {
348         _webView = webView;
349         _shouldEnsureUIApplication = NO;
350         _shouldBecomeFirstResponder = YES;
351         _shouldAllowMoveOperation = YES;
352         [_webView setUIDelegate:self];
353         [_webView _setInputDelegate:self];
354         self.dragDestinationAction = WKDragDestinationActionAny & ~WKDragDestinationActionLoad;
355     }
356     return self;
357 }
358
359 - (void)dealloc
360 {
361     if ([_webView UIDelegate] == self)
362         [_webView setUIDelegate:nil];
363
364     if ([_webView _inputDelegate] == self)
365         [_webView _setInputDelegate:nil];
366
367     [super dealloc];
368 }
369
370 - (void)_resetSimulatedState
371 {
372     _suppressedSelectionCommandsDuringDrop = NO;
373     _phase = DragAndDropPhaseBeginning;
374     _currentProgress = 0;
375     _isDoneWithCurrentRun = false;
376     _observedEventNames = adoptNS([[NSMutableArray alloc] init]);
377     _insertedAttachments = adoptNS([[NSMutableArray alloc] init]);
378     _removedAttachments = adoptNS([[NSMutableArray alloc] init]);
379     _finalSelectionRects = @[ ];
380     _dragSession = nil;
381     _dropSession = nil;
382     _lastKnownDropProposal = nil;
383     _lastKnownDragCaretRect = CGRectZero;
384     _remainingAdditionalItemRequestLocationsByProgress = nil;
385     _queuedAdditionalItemRequestLocations = adoptNS([[NSMutableArray alloc] init]);
386     _liftPreviews = adoptNS([[NSMutableArray alloc] init]);
387     _hasStartedInputSession = false;
388 }
389
390 - (NSArray *)observedEventNames
391 {
392     return _observedEventNames.get();
393 }
394
395 - (UIDropProposal *)lastKnownDropProposal
396 {
397     return _lastKnownDropProposal.get();
398 }
399
400 - (void)simulateAllTouchesCanceled:(NSNotification *)notification
401 {
402     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_advanceProgress) object:nil];
403     _phase = DragAndDropPhaseCancelled;
404     _currentProgress = 1;
405     _isDoneWithCurrentRun = true;
406     if (_dragSession)
407         [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:UIDropOperationCancel];
408 }
409
410 - (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation
411 {
412     [self runFrom:startLocation to:endLocation additionalItemRequestLocations:nil];
413 }
414
415 - (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation additionalItemRequestLocations:(ProgressToCGPointValueMap)additionalItemRequestLocations
416 {
417     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
418     [defaultCenter addObserver:self selector:@selector(simulateAllTouchesCanceled:) name:TestWebKitAPISimulateCancelAllTouchesNotificationName object:nil];
419
420     if (_shouldEnsureUIApplication)
421         UIApplicationInstantiateSingleton([DragAndDropSimulatorApplication class]);
422
423     if (_shouldBecomeFirstResponder)
424         [_webView becomeFirstResponder];
425
426     [self _resetSimulatedState];
427
428     if (additionalItemRequestLocations)
429         _remainingAdditionalItemRequestLocationsByProgress = adoptNS([additionalItemRequestLocations mutableCopy]);
430
431     for (NSString *eventName in dragAndDropEventNames()) {
432         [_webView performAfterReceivingMessage:eventName action:[strongSelf = retainPtr(self), name = retainPtr(eventName)] {
433             [strongSelf->_observedEventNames addObject:name.get()];
434         }];
435     }
436
437     _startLocation = startLocation;
438     _endLocation = endLocation;
439
440     if (self.externalItemProviders.count) {
441         _dropSession = adoptNS([[MockDropSession alloc] initWithProviders:self.externalItemProviders location:_startLocation window:[_webView window] allowMove:self.shouldAllowMoveOperation]);
442         _phase = DragAndDropPhaseBegan;
443         [self _advanceProgress];
444     } else {
445         _dragSession = adoptNS([[MockDragSession alloc] initWithWindow:[_webView window] allowMove:self.shouldAllowMoveOperation]);
446         [_dragSession setMockLocationInWindow:_startLocation];
447         [(id <UIDragInteractionDelegate_ForWebKitOnly>)[_webView dragInteractionDelegate] _dragInteraction:[_webView dragInteraction] prepareForSession:_dragSession.get() completion:[strongSelf = retainPtr(self)] {
448             if (strongSelf->_phase == DragAndDropPhaseCancelled)
449                 return;
450
451             strongSelf->_phase = DragAndDropPhaseBeginning;
452             [strongSelf _advanceProgress];
453         }];
454     }
455
456     Util::run(&_isDoneWithCurrentRun);
457     [_webView clearMessageHandlers:dragAndDropEventNames()];
458     _finalSelectionRects = [_webView selectionRectsAfterPresentationUpdate];
459
460     [defaultCenter removeObserver:self];
461 }
462
463 - (NSArray *)finalSelectionRects
464 {
465     return _finalSelectionRects.get();
466 }
467
468 - (void)_concludeDropAndPerformOperationIfNecessary
469 {
470     _lastKnownDragCaretRect = [_webView _dragCaretRect];
471     auto operation = [_lastKnownDropProposal operation];
472     if (operation != UIDropOperationCancel && operation != UIDropOperationForbidden) {
473         [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] performDrop:_dropSession.get()];
474         _phase = DragAndDropPhasePerformingDrop;
475     } else {
476         _isDoneWithCurrentRun = YES;
477         _phase = DragAndDropPhaseCancelled;
478     }
479
480     [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] sessionDidEnd:_dropSession.get()];
481
482     if (_dragSession) {
483         auto delegate = [_webView dragInteractionDelegate];
484         [delegate dragInteraction:[_webView dragInteraction] session:_dragSession.get() didEndWithOperation:operation];
485         if ([delegate respondsToSelector:@selector(_clearToken:)])
486             [(id <UITextInputMultiDocument>)delegate _clearToken:nil];
487         [_webView becomeFirstResponder];
488     }
489 }
490
491 - (void)_enqueuePendingAdditionalItemRequestLocations
492 {
493     NSMutableArray *progressValuesToRemove = [NSMutableArray array];
494     for (NSNumber *progressValue in _remainingAdditionalItemRequestLocationsByProgress.get()) {
495         double progress = progressValue.doubleValue;
496         if (progress > _currentProgress)
497             continue;
498         [progressValuesToRemove addObject:progressValue];
499         [_queuedAdditionalItemRequestLocations addObject:[_remainingAdditionalItemRequestLocationsByProgress objectForKey:progressValue]];
500     }
501
502     for (NSNumber *progressToRemove in progressValuesToRemove)
503         [_remainingAdditionalItemRequestLocationsByProgress removeObjectForKey:progressToRemove];
504 }
505
506 - (BOOL)_sendQueuedAdditionalItemRequest
507 {
508     if (![_queuedAdditionalItemRequestLocations count])
509         return NO;
510
511     RetainPtr<NSValue> requestLocationValue = [_queuedAdditionalItemRequestLocations objectAtIndex:0];
512     [_queuedAdditionalItemRequestLocations removeObjectAtIndex:0];
513
514     auto requestLocation = [[_webView window] convertPoint:[requestLocationValue CGPointValue] toView:_webView.get()];
515     [(id <UIDragInteractionDelegate_ForWebKitOnly>)[_webView dragInteractionDelegate] _dragInteraction:[_webView dragInteraction] itemsForAddingToSession:_dragSession.get() withTouchAtPoint:requestLocation completion:[dragSession = _dragSession, dropSession = _dropSession] (NSArray *items) {
516         [dragSession addItems:items];
517         [dropSession addItems:items];
518     }];
519     return YES;
520 }
521
522 - (void)_advanceProgress
523 {
524     [self _enqueuePendingAdditionalItemRequestLocations];
525     if ([self _sendQueuedAdditionalItemRequest]) {
526         [self _scheduleAdvanceProgress];
527         return;
528     }
529
530     _lastKnownDragCaretRect = [_webView _dragCaretRect];
531     _currentProgress += progressIncrementStep;
532     CGPoint locationInWindow = self._currentLocation;
533     [_dragSession setMockLocationInWindow:locationInWindow];
534     [_dropSession setMockLocationInWindow:locationInWindow];
535
536     if (_currentProgress >= 1) {
537         _currentProgress = 1;
538         [self _concludeDropAndPerformOperationIfNecessary];
539         return;
540     }
541
542     switch (_phase) {
543     case DragAndDropPhaseBeginning: {
544         NSMutableArray<NSItemProvider *> *itemProviders = [NSMutableArray array];
545         NSArray *items = [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] itemsForBeginningSession:_dragSession.get()];
546         if (!items.count) {
547             _phase = DragAndDropPhaseCancelled;
548             _currentProgress = 1;
549             _isDoneWithCurrentRun = true;
550             return;
551         }
552
553         for (UIDragItem *item in items) {
554             [itemProviders addObject:item.itemProvider];
555             UITargetedDragPreview *liftPreview = [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] previewForLiftingItem:item session:_dragSession.get()];
556             EXPECT_TRUE(!!liftPreview);
557             if (liftPreview)
558                 [_liftPreviews addObject:liftPreview];
559         }
560
561         _dropSession = adoptNS([[MockDropSession alloc] initWithProviders:itemProviders location:self._currentLocation window:[_webView window] allowMove:self.shouldAllowMoveOperation]);
562         [_dragSession setItems:items];
563         _sourceItemProviders = itemProviders;
564         if (self.showCustomActionSheetBlock) {
565             // Defer progress until the custom action sheet is dismissed.
566             auto startLocationInView = [[_webView window] convertPoint:_startLocation toView:_webView.get()];
567             [_webView _simulateLongPressActionAtLocation:startLocationInView];
568             return;
569         }
570
571         auto delegate = [_webView dragInteractionDelegate];
572         if ([delegate respondsToSelector:@selector(_preserveFocusWithToken:destructively:)])
573             [(id <UITextInputMultiDocument>)delegate _preserveFocusWithToken:nil destructively:NO];
574
575         [_webView resignFirstResponder];
576
577         [delegate dragInteraction:[_webView dragInteraction] sessionWillBegin:_dragSession.get()];
578
579         RetainPtr<WKWebView> retainedWebView = _webView;
580         dispatch_async(dispatch_get_main_queue(), ^() {
581             [retainedWebView resignFirstResponder];
582         });
583
584         _phase = DragAndDropPhaseBegan;
585         break;
586     }
587     case DragAndDropPhaseBegan:
588         [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] sessionDidEnter:_dropSession.get()];
589         _phase = DragAndDropPhaseEntered;
590         break;
591     case DragAndDropPhaseEntered: {
592         _lastKnownDropProposal = [[_webView dropInteractionDelegate] dropInteraction:[_webView dropInteraction] sessionDidUpdate:_dropSession.get()];
593         if (![self shouldAllowMoveOperation] && [_lastKnownDropProposal operation] == UIDropOperationMove)
594             _lastKnownDropProposal = adoptNS([[UIDropProposal alloc] initWithDropOperation:UIDropOperationCancel]);
595         break;
596     }
597     default:
598         break;
599     }
600
601     [self _scheduleAdvanceProgress];
602 }
603
604 - (void)clearExternalDragInformation
605 {
606     _externalItemProviders = nil;
607 }
608
609 - (CGPoint)_currentLocation
610 {
611     CGFloat distanceX = _endLocation.x - _startLocation.x;
612     CGFloat distanceY = _endLocation.y - _startLocation.y;
613     return CGPointMake(_startLocation.x + _currentProgress * distanceX, _startLocation.y + _currentProgress * distanceY);
614 }
615
616 - (void)_scheduleAdvanceProgress
617 {
618     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_advanceProgress) object:nil];
619     [self performSelector:@selector(_advanceProgress) withObject:nil afterDelay:progressTimeStep];
620 }
621
622 - (NSArray *)sourceItemProviders
623 {
624     return _sourceItemProviders.get();
625 }
626
627 - (NSArray *)externalItemProviders
628 {
629     return _externalItemProviders.get();
630 }
631
632 - (void)setExternalItemProviders:(NSArray *)externalItemProviders
633 {
634     _externalItemProviders = adoptNS([externalItemProviders copy]);
635 }
636
637 - (DragAndDropPhase)phase
638 {
639     return _phase;
640 }
641
642 - (NSArray<UITargetedDragPreview *> *)liftPreviews
643 {
644     return _liftPreviews.get();
645 }
646
647 - (CGRect)lastKnownDragCaretRect
648 {
649     return _lastKnownDragCaretRect;
650 }
651
652 - (void)ensureInputSession
653 {
654     Util::run(&_hasStartedInputSession);
655 }
656
657 - (NSArray<_WKAttachment *> *)insertedAttachments
658 {
659     return _insertedAttachments.get();
660 }
661
662 - (NSArray<_WKAttachment *> *)removedAttachments
663 {
664     return _removedAttachments.get();
665 }
666
667 - (void)endDataTransfer
668 {
669     [[_webView dragInteractionDelegate] dragInteraction:[_webView dragInteraction] sessionDidTransferItems:_dragSession.get()];
670 }
671
672 - (TestWKWebView *)webView
673 {
674     return _webView.get();
675 }
676
677 - (void)setShowCustomActionSheetBlock:(BOOL(^)(_WKActivatedElementInfo *))showCustomActionSheetBlock
678 {
679     _showCustomActionSheetBlock = showCustomActionSheetBlock;
680 }
681
682 - (BOOL(^)(_WKActivatedElementInfo *))showCustomActionSheetBlock
683 {
684     return _showCustomActionSheetBlock.get();
685 }
686
687 - (void)setConvertItemProvidersBlock:(NSArray *(^)(NSItemProvider *, NSArray *, NSDictionary *))convertItemProvidersBlock
688 {
689     _convertItemProvidersBlock = convertItemProvidersBlock;
690 }
691
692 - (NSArray *(^)(NSItemProvider *, NSArray *, NSDictionary *))convertItemProvidersBlock
693 {
694     return _convertItemProvidersBlock.get();
695 }
696
697 - (void)setOverridePerformDropBlock:(NSArray *(^)(id <UIDropSession>))overridePerformDropBlock
698 {
699     _overridePerformDropBlock = overridePerformDropBlock;
700 }
701
702 - (NSArray *(^)(id <UIDropSession>))overridePerformDropBlock
703 {
704     return _overridePerformDropBlock.get();
705 }
706
707 - (void)setOverrideDragUpdateBlock:(UIDropOperation(^)(UIDropOperation, id <UIDropSession>))overrideDragUpdateBlock
708 {
709     _overrideDragUpdateBlock = overrideDragUpdateBlock;
710 }
711
712 - (UIDropOperation(^)(UIDropOperation, id <UIDropSession>))overrideDragUpdateBlock
713 {
714     return _overrideDragUpdateBlock.get();
715 }
716
717 - (void)setDropCompletionBlock:(void(^)(BOOL, NSArray *))dropCompletionBlock
718 {
719     _dropCompletionBlock = dropCompletionBlock;
720 }
721
722 - (void(^)(BOOL, NSArray *))dropCompletionBlock
723 {
724     return _dropCompletionBlock.get();
725 }
726
727 #pragma mark - WKUIDelegatePrivate
728
729 - (void)_webView:(WKWebView *)webView dataInteractionOperationWasHandled:(BOOL)handled forSession:(id)session itemProviders:(NSArray<NSItemProvider *> *)itemProviders
730 {
731     _suppressedSelectionCommandsDuringDrop = [_webView textInputContentView]._shouldSuppressSelectionCommands;
732     _isDoneWithCurrentRun = true;
733
734     if (self.dropCompletionBlock)
735         self.dropCompletionBlock(handled, itemProviders);
736 }
737
738 - (UIDropProposal *)_webView:(WKWebView *)webView willUpdateDropProposalToProposal:(UIDropProposal *)proposal forSession:(id <UIDropSession>)session
739 {
740     if (!self.overrideDragUpdateBlock)
741         return proposal;
742
743     return [[[UIDropProposal alloc] initWithDropOperation:self.overrideDragUpdateBlock(proposal.operation, session)] autorelease];
744 }
745
746 - (NSArray *)_webView:(WKWebView *)webView adjustedDataInteractionItemProvidersForItemProvider:(NSItemProvider *)itemProvider representingObjects:(NSArray *)representingObjects additionalData:(NSDictionary *)additionalData
747 {
748     return self.convertItemProvidersBlock ? self.convertItemProvidersBlock(itemProvider, representingObjects, additionalData) : @[ itemProvider ];
749 }
750
751 - (BOOL)_webView:(WKWebView *)webView showCustomSheetForElement:(_WKActivatedElementInfo *)element
752 {
753     if (!self.showCustomActionSheetBlock)
754         return NO;
755
756     dispatch_async(dispatch_get_main_queue(), [strongSelf = retainPtr(self)] {
757         [[strongSelf->_webView dragInteractionDelegate] dragInteraction:[strongSelf->_webView dragInteraction] sessionWillBegin:strongSelf->_dragSession.get()];
758         strongSelf->_phase = DragAndDropPhaseBegan;
759         [strongSelf _scheduleAdvanceProgress];
760     });
761
762     return self.showCustomActionSheetBlock(element);
763 }
764
765 - (NSArray<UIDragItem *> *)_webView:(WKWebView *)webView willPerformDropWithSession:(id <UIDropSession>)session
766 {
767     return self.overridePerformDropBlock ? self.overridePerformDropBlock(session) : session.items;
768 }
769
770 - (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment withSource:(NSString *)source
771 {
772     [_insertedAttachments addObject:attachment];
773 }
774
775 - (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment
776 {
777     [_removedAttachments addObject:attachment];
778 }
779
780 - (WKDragDestinationAction)_webView:(WKWebView *)webView dragDestinationActionMaskForDraggingInfo:(id)draggingInfo
781 {
782     return self.dragDestinationAction;
783 }
784
785 #pragma mark - _WKInputDelegate
786
787 - (BOOL)_webView:(WKWebView *)webView focusShouldStartInputSession:(id <_WKFocusedElementInfo>)info
788 {
789     return _allowsFocusToStartInputSession;
790 }
791
792 - (void)_webView:(WKWebView *)webView didStartInputSession:(id <_WKFormInputSession>)inputSession
793 {
794     _hasStartedInputSession = true;
795 }
796
797 @end
798
799 #endif // ENABLE(DRAG_SUPPORT) && PLATFORM(IOS_FAMILY)