| /* |
| * Copyright (C) 2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "DataInteractionSimulator.h" |
| |
| #if ENABLE(DATA_INTERACTION) |
| |
| #import "PlatformUtilities.h" |
| #import <UIKit/UIItemProvider_Private.h> |
| #import <WebKit/WKWebViewPrivate.h> |
| #import <wtf/RetainPtr.h> |
| |
| using namespace TestWebKitAPI; |
| |
| #if USE(APPLE_INTERNAL_SDK) && __has_include(<WebKitAdditions/DataInteractionSimulatorAdditions.mm>) |
| #include <WebKitAdditions/DataInteractionSimulatorAdditions.mm> |
| #endif |
| |
| static double progressIncrementStep = 0.033; |
| static double progressTimeStep = 0.016; |
| |
| static NSArray *dataInteractionEventNames() |
| { |
| static NSArray *eventNames = nil; |
| static dispatch_once_t onceToken; |
| dispatch_once(&onceToken, ^() { |
| eventNames = @[ DataInteractionEnterEventName, DataInteractionOverEventName, DataInteractionPerformOperationEventName, DataInteractionLeaveEventName, DataInteractionStartEventName ]; |
| }); |
| return eventNames; |
| } |
| |
| @implementation DataInteractionSimulator |
| |
| - (instancetype)initWithWebView:(TestWKWebView *)webView |
| { |
| if (self = [super init]) { |
| _webView = webView; |
| [_webView _setTestingDelegate:self]; |
| [_webView setUIDelegate:self]; |
| } |
| return self; |
| } |
| |
| - (void)dealloc |
| { |
| if ([_webView _testingDelegate] == self) |
| [_webView _setTestingDelegate:nil]; |
| |
| if ([_webView UIDelegate] == self) |
| [_webView setUIDelegate:nil]; |
| |
| [super dealloc]; |
| } |
| |
| - (void)_resetSimulatedState |
| { |
| _phase = DataInteractionBeginning; |
| _currentProgress = 0; |
| _isDoneWithCurrentRun = false; |
| _observedEventNames = adoptNS([[NSMutableArray alloc] init]); |
| _finalSelectionRects = @[ ]; |
| _dataInteractionSession = nil; |
| _dataOperationSession = nil; |
| } |
| |
| - (NSArray *)observedEventNames |
| { |
| return _observedEventNames.get(); |
| } |
| |
| - (void)runFrom:(CGPoint)startLocation to:(CGPoint)endLocation |
| { |
| [self _resetSimulatedState]; |
| |
| RetainPtr<DataInteractionSimulator> strongSelf = self; |
| for (NSString *eventName in dataInteractionEventNames()) { |
| DataInteractionSimulator *weakSelf = strongSelf.get(); |
| [weakSelf->_webView performAfterReceivingMessage:eventName action:^() { |
| [weakSelf->_observedEventNames addObject:eventName]; |
| }]; |
| } |
| |
| _startLocation = startLocation; |
| _endLocation = endLocation; |
| |
| if (self.externalItemProvider) { |
| _dataOperationSession = adoptNS([[MockDataOperationSession alloc] initWithProvider:self.externalItemProvider location:_startLocation window:[_webView window]]); |
| _phase = DataInteractionBegan; |
| [self _advanceProgress]; |
| } else { |
| _dataInteractionSession = adoptNS([[MockDataInteractionSession alloc] initWithWindow:[_webView window]]); |
| [_dataInteractionSession setMockLocationInWindow:_startLocation]; |
| [_webView _simulatePrepareForDataInteractionSession:_dataInteractionSession.get() completion:^() { |
| DataInteractionSimulator *weakSelf = strongSelf.get(); |
| weakSelf->_phase = DataInteractionBeginning; |
| [weakSelf _advanceProgress]; |
| }]; |
| } |
| |
| Util::run(&_isDoneWithCurrentRun); |
| [_webView clearMessageHandlers:dataInteractionEventNames()]; |
| _finalSelectionRects = [_webView selectionRectsAfterPresentationUpdate]; |
| } |
| |
| - (NSArray *)finalSelectionRects |
| { |
| return _finalSelectionRects.get(); |
| } |
| |
| - (void)_advanceProgress |
| { |
| _currentProgress += progressIncrementStep; |
| CGPoint locationInWindow = self._currentLocation; |
| [_dataInteractionSession setMockLocationInWindow:locationInWindow]; |
| [_dataOperationSession setMockLocationInWindow:locationInWindow]; |
| |
| if (_currentProgress >= 1) { |
| [_webView _simulateDataInteractionPerformOperation:_dataOperationSession.get()]; |
| [_webView _simulateDataInteractionEnded:_dataOperationSession.get()]; |
| if (_dataInteractionSession) |
| [_webView _simulateDataInteractionSessionDidEnd:_dataInteractionSession.get()]; |
| |
| _phase = DataInteractionPerforming; |
| _currentProgress = 1; |
| return; |
| } |
| |
| switch (_phase) { |
| case DataInteractionBeginning: { |
| NSMutableArray<UIItemProvider *> *itemProviders = [NSMutableArray array]; |
| NSArray *items = [_webView _simulatedItemsForSession:_dataInteractionSession.get()]; |
| if (!items.count) { |
| _phase = DataInteractionCancelled; |
| _currentProgress = 1; |
| _isDoneWithCurrentRun = true; |
| return; |
| } |
| |
| #if HAS_DATA_INTERACTION_ITEMS |
| for (WKDataInteractionItem *item in items) |
| [itemProviders addObject:item.itemProvider]; |
| #endif |
| |
| _dataOperationSession = adoptNS([[MockDataOperationSession alloc] initWithProvider:itemProviders.firstObject location:self._currentLocation window:[_webView window]]); |
| #if HAS_DATA_INTERACTION_ITEMS |
| [_dataInteractionSession setItems:items]; |
| #endif |
| [_webView _simulateWillBeginDataInteractionWithSession:_dataInteractionSession.get()]; |
| _phase = DataInteractionBegan; |
| break; |
| } |
| case DataInteractionBegan: |
| [_webView _simulateDataInteractionEntered:_dataOperationSession.get()]; |
| _phase = DataInteractionEntered; |
| break; |
| case DataInteractionEntered: |
| [_webView _simulateDataInteractionUpdated:_dataOperationSession.get()]; |
| break; |
| default: |
| break; |
| } |
| |
| [self _scheduleAdvanceProgress]; |
| } |
| |
| - (CGPoint)_currentLocation |
| { |
| CGFloat distanceX = _endLocation.x - _startLocation.x; |
| CGFloat distanceY = _endLocation.y - _startLocation.y; |
| return CGPointMake(_startLocation.x + _currentProgress * distanceX, _startLocation.y + _currentProgress * distanceY); |
| } |
| |
| - (void)_scheduleAdvanceProgress |
| { |
| [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_advanceProgress) object:nil]; |
| [self performSelector:@selector(_advanceProgress) withObject:nil afterDelay:progressTimeStep]; |
| } |
| |
| - (UIItemProvider *)externalItemProvider |
| { |
| return _externalItemProvider.get(); |
| } |
| |
| - (void)setExternalItemProvider:(UIItemProvider *)externalItemProvider |
| { |
| _externalItemProvider = externalItemProvider; |
| } |
| |
| #pragma mark - _WKTestingDelegate |
| |
| - (void)webViewDidPerformDataInteractionControllerOperation:(WKWebView *)webView |
| { |
| _isDoneWithCurrentRun = true; |
| } |
| |
| #pragma mark - WKUIDelegatePrivate |
| |
| - (NSArray<UIItemProvider *>*)_webView:(WKWebView *)webView adjustedDataInteractionItemProviders:(NSArray<UIItemProvider *>*)originalItemProviders |
| { |
| return self.convertItemProvidersBlock ? self.convertItemProvidersBlock(originalItemProviders) : originalItemProviders; |
| } |
| |
| @end |
| |
| #endif // ENABLE(DATA_INTERACTION) |