/* * 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. */ #import "config.h" #if ENABLE(DRAG_SUPPORT) && PLATFORM(IOS_FAMILY) && !PLATFORM(MACCATALYST) #import "ClassMethodSwizzler.h" #import "DragAndDropSimulator.h" #import "NSItemProviderAdditions.h" #import "PlatformUtilities.h" #import "TestURLSchemeHandler.h" #import "TestWKWebView.h" #import "UIKitSPI.h" #import "WKWebViewConfigurationExtras.h" #import #import #import #import #import #import #import #import #import #import #import #import #import SOFT_LINK_FRAMEWORK(Contacts) SOFT_LINK_CLASS(Contacts, CNMutableContact) SOFT_LINK_FRAMEWORK(MapKit) SOFT_LINK_CLASS(MapKit, MKMapItem) SOFT_LINK_CLASS(MapKit, MKPlacemark) #if !USE(APPLE_INTERNAL_SDK) @interface NSItemProviderRepresentationOptions : NSObject @end #endif @interface NSItemProvider () + (NSItemProvider *)itemProviderWithURL:(NSURL *)url title:(NSString *)title; @end static NSString *InjectedBundlePasteboardDataType = @"org.webkit.data"; static UIImage *testIconImage() { return [UIImage imageNamed:@"TestWebKitAPI.resources/icon.png"]; } static NSData *testZIPArchive() { NSURL *zipFileURL = [[NSBundle mainBundle] URLForResource:@"compressed-files" withExtension:@"zip" subdirectory:@"TestWebKitAPI.resources"]; return [NSData dataWithContentsOfURL:zipFileURL]; } @implementation TestWKWebView (DragAndDropTestsIOS) - (BOOL)editorContainsImageElement { return [self stringByEvaluatingJavaScript:@"!!editor.querySelector('img')"].boolValue; } - (NSString *)editorValue { return [self stringByEvaluatingJavaScript:@"editor.value"]; } @end @interface ModelLoadingMessageHandler : NSObject @property (nonatomic) BOOL didLoadModel; @end @implementation ModelLoadingMessageHandler - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { EXPECT_WK_STREQ(@"READY", [message body]); _didLoadModel = true; } @end static void loadTestPageAndEnsureInputSession(DragAndDropSimulator *simulator, NSString *testPageName) { TestWKWebView *webView = [simulator webView]; simulator.allowsFocusToStartInputSession = YES; [webView becomeFirstResponder]; [webView synchronouslyLoadTestPageNamed:testPageName]; [simulator ensureInputSession]; } static void checkCGRectIsEqualToCGRectWithLogging(CGRect expected, CGRect observed) { BOOL isEqual = CGRectEqualToRect(expected, observed); EXPECT_TRUE(isEqual); if (!isEqual) NSLog(@"Expected: %@ but observed: %@", NSStringFromCGRect(expected), NSStringFromCGRect(observed)); } static void checkRichTextTypePrecedesPlainTextType(DragAndDropSimulator *simulator) { // At least one of "com.apple.flat-rtfd" or "public.rtf" is expected to have higher precedence than "public.utf8-plain-text". NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers]; auto indexOfRTFType = [registeredTypes indexOfObject:(__bridge NSString *)kUTTypeRTF]; auto indexOfFlatRTFDType = [registeredTypes indexOfObject:(__bridge NSString *)kUTTypeFlatRTFD]; auto indexOfPlainTextType = [registeredTypes indexOfObject:(__bridge NSString *)kUTTypeUTF8PlainText]; EXPECT_NE((NSInteger)indexOfPlainTextType, NSNotFound); EXPECT_TRUE((indexOfRTFType != NSNotFound && indexOfRTFType < indexOfPlainTextType) || (indexOfFlatRTFDType != NSNotFound && indexOfFlatRTFDType < indexOfPlainTextType)); } static void checkFirstTypeIsPresentAndSecondTypeIsMissing(DragAndDropSimulator *simulator, CFStringRef firstType, CFStringRef secondType) { NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers]; EXPECT_TRUE([registeredTypes containsObject:(NSString *)firstType]); EXPECT_FALSE([registeredTypes containsObject:(NSString *)secondType]); } static void checkTypeIdentifierIsRegisteredAtIndex(DragAndDropSimulator *simulator, NSString *type, NSUInteger index) { NSArray *registeredTypes = [simulator.sourceItemProviders.firstObject registeredTypeIdentifiers]; EXPECT_GT(registeredTypes.count, index); EXPECT_WK_STREQ(type.UTF8String, [registeredTypes[index] UTF8String]); } static void checkEstimatedSize(DragAndDropSimulator *simulator, CGSize estimatedSize) { NSItemProvider *sourceItemProvider = [simulator sourceItemProviders].firstObject; EXPECT_EQ(estimatedSize.width, sourceItemProvider.preferredPresentationSize.width); EXPECT_EQ(estimatedSize.height, sourceItemProvider.preferredPresentationSize.height); } static void checkSuggestedNameAndEstimatedSize(DragAndDropSimulator *simulator, NSString *suggestedName, CGSize estimatedSize) { NSItemProvider *sourceItemProvider = [simulator sourceItemProviders].firstObject; EXPECT_WK_STREQ(suggestedName.UTF8String, sourceItemProvider.suggestedName.UTF8String); EXPECT_EQ(estimatedSize.width, sourceItemProvider.preferredPresentationSize.width); EXPECT_EQ(estimatedSize.height, sourceItemProvider.preferredPresentationSize.height); } static void checkStringArraysAreEqual(NSArray *expected, NSArray *observed) { EXPECT_EQ(expected.count, observed.count); for (NSUInteger index = 0; index < expected.count; ++index) { NSString *expectedString = [expected objectAtIndex:index]; NSString *observedString = [observed objectAtIndex:index]; EXPECT_WK_STREQ(expectedString, observedString); if (![expectedString isEqualToString:observedString]) NSLog(@"Expected observed string: %@ to match expected string: %@ at index: %tu", observedString, expectedString, index); } } 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)); } static void checkJSONWithLogging(NSString *jsonString, NSDictionary *expected) { BOOL success = TestWebKitAPI::Util::jsonMatchesExpectedValues(jsonString, expected); EXPECT_TRUE(success); if (!success) NSLog(@"Expected JSON: %@ to match values: %@", jsonString, expected); } static NSData *testIconImageData() { return [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"icon" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"]]; } static void runTestWithTemporaryTextFile(void(^runTest)(NSURL *fileURL)) { NSString *fileName = [NSString stringWithFormat:@"drag-drop-text-file-%@.txt", [NSUUID UUID].UUIDString]; RetainPtr temporaryFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName] isDirectory:NO]; [[NSFileManager defaultManager] removeItemAtURL:temporaryFile.get() error:nil]; NSError *error = nil; [@"This is a tiny blob of text." writeToURL:temporaryFile.get() atomically:YES encoding:NSUTF8StringEncoding error:&error]; if (error) NSLog(@"Error writing temporary file: %@", error); @try { runTest(temporaryFile.get()); } @finally { [[NSFileManager defaultManager] removeItemAtURL:temporaryFile.get() error:nil]; } } static void runTestWithTemporaryFolder(void(^runTest)(NSURL *folderURL)) { NSString *folderName = [NSString stringWithFormat:@"some.directory-%@", [NSUUID UUID].UUIDString]; RetainPtr temporaryFolder = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:folderName] isDirectory:YES]; [[NSFileManager defaultManager] removeItemAtURL:temporaryFolder.get() error:nil]; NSError *error = nil; NSFileManager *defaultManager = [NSFileManager defaultManager]; [defaultManager createDirectoryAtURL:temporaryFolder.get() withIntermediateDirectories:NO attributes:nil error:&error]; [testIconImageData() writeToURL:[temporaryFolder.get() URLByAppendingPathComponent:@"icon.png" isDirectory:NO] atomically:YES]; [testZIPArchive() writeToURL:[temporaryFolder.get() URLByAppendingPathComponent:@"archive.zip" isDirectory:NO] atomically:YES]; NSURL *firstSubdirectory = [temporaryFolder.get() URLByAppendingPathComponent:@"subdirectory1" isDirectory:YES]; [defaultManager createDirectoryAtURL:firstSubdirectory withIntermediateDirectories:NO attributes:nil error:&error]; [@"I am a text file in the first subdirectory." writeToURL:[firstSubdirectory URLByAppendingPathComponent:@"text-file-1.txt" isDirectory:NO] atomically:YES encoding:NSUTF8StringEncoding error:&error]; NSURL *secondSubdirectory = [temporaryFolder.get() URLByAppendingPathComponent:@"subdirectory2" isDirectory:YES]; [defaultManager createDirectoryAtURL:secondSubdirectory withIntermediateDirectories:NO attributes:nil error:&error]; [@"I am a text file in the second subdirectory." writeToURL:[secondSubdirectory URLByAppendingPathComponent:@"text-file-2.txt" isDirectory:NO] atomically:YES encoding:NSUTF8StringEncoding error:&error]; if (error) NSLog(@"Error writing temporary file: %@", error); @try { runTest(temporaryFolder.get()); } @finally { [[NSFileManager defaultManager] removeItemAtURL:temporaryFolder.get() error:nil]; } } namespace TestWebKitAPI { TEST(DragAndDropTests, ImageToContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_TRUE([webView editorContainsImageElement]); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); EXPECT_TRUE([observedEventNames containsObject:@"drop"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(214, 201, 2, 174), [simulator finalSelectionStartRect]); checkFirstTypeIsPresentAndSecondTypeIsMissing(simulator.get(), kUTTypePNG, kUTTypeFileURL); checkEstimatedSize(simulator.get(), { 215, 174 }); EXPECT_TRUE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, CanStartDragOnEnormousImage) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadHTMLString:@""]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 100)]; NSArray *registeredTypes = [[simulator sourceItemProviders].firstObject registeredTypeIdentifiers]; EXPECT_WK_STREQ((__bridge NSString *)kUTTypeScalableVectorGraphics, [registeredTypes firstObject]); } TEST(DragAndDropTests, ImageToTextarea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"image-and-textarea"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("", [webView editorValue]); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); EXPECT_TRUE([observedEventNames containsObject:@"drop"]); checkFirstTypeIsPresentAndSecondTypeIsMissing(simulator.get(), kUTTypePNG, kUTTypeFileURL); checkEstimatedSize(simulator.get(), { 215, 174 }); } TEST(DragAndDropTests, ImageInLinkToInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(2069, 241, 2, 240), [simulator finalSelectionStartRect]); checkSuggestedNameAndEstimatedSize(simulator.get(), @"icon.png", { 215, 174 }); checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypePNG, 0); EXPECT_TRUE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, AvoidPreciseDropNearTopOfTextArea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadHTMLString:@""]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto firstItem = adoptNS([[NSItemProvider alloc] initWithObject:[NSURL URLWithString:@"https://webkit.org"]]); [simulator setExternalItemProviders:@[ firstItem.get() ]]; [simulator runFrom:CGPointMake(320, 10) to:CGPointMake(20, 10)]; EXPECT_WK_STREQ("https://webkit.org/", [webView stringByEvaluatingJavaScript:@"document.querySelector('textarea').value"]); EXPECT_FALSE([simulator lastKnownDropProposal].precise); [webView evaluateJavaScript:@"document.querySelector('textarea').value = ''" completionHandler:nil]; auto secondItem = adoptNS([[NSItemProvider alloc] initWithObject:[NSURL URLWithString:@"https://apple.com"]]); [simulator setExternalItemProviders:@[ secondItem.get() ]]; [simulator runFrom:CGPointMake(320, 50) to:CGPointMake(20, 50)]; EXPECT_WK_STREQ("https://apple.com/", [webView stringByEvaluatingJavaScript:@"document.querySelector('textarea').value"]); EXPECT_TRUE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ImageInLinkWithoutHREFToInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"image-in-link-and-input"]; [webView stringByEvaluatingJavaScript:@"link.href = ''"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("", [webView editorValue]); checkEstimatedSize(simulator.get(), { 215, 174 }); checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypePNG, 0); } TEST(DragAndDropTests, ImageDoesNotUseElementSizeAsEstimatedSize) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"gif-and-file-input"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom: { 100, 100 } to: { 100, 300 }]; checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypeGIF, 0); checkSuggestedNameAndEstimatedSize(simulator.get(), @"apple.gif", { 52, 64 }); EXPECT_WK_STREQ("apple.gif (image/gif)", [webView stringByEvaluatingJavaScript:@"output.textContent"]); } TEST(DragAndDropTests, ContentEditableToContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); loadTestPageAndEnsureInputSession(simulator.get(), @"autofocus-contenteditable"); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL); EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); EXPECT_TRUE([observedEventNames containsObject:@"drop"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(960, 205, 2, 223), [simulator finalSelectionStartRect]); checkRichTextTypePrecedesPlainTextType(simulator.get()); EXPECT_TRUE([simulator lastKnownDropProposal].precise); // FIXME: Once is fixed, we should add "com.apple.webarchive" as a registered pasteboard type and rebaseline this expectation. EXPECT_FALSE([[[simulator sourceItemProviders].firstObject registeredTypeIdentifiers] containsObject:(__bridge NSString *)kUTTypeWebArchive]); } TEST(DragAndDropTests, ContentEditableToTextarea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); loadTestPageAndEnsureInputSession(simulator.get(), @"contenteditable-and-textarea"); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.textContent"].length, 0UL); EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); EXPECT_TRUE([observedEventNames containsObject:@"drop"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(1033, 203, 2, 240), [simulator finalSelectionStartRect]); checkRichTextTypePrecedesPlainTextType(simulator.get()); EXPECT_TRUE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, NonEditableTextSelectionToTextarea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView synchronouslyLoadTestPageNamed:@"selected-text-and-textarea"]; [simulator runFrom:CGPointMake(160, 100) to:CGPointMake(160, 300)]; EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"destination.value"]); } TEST(DragAndDropTests, DoNotPerformSelectionDragWhenNotFirstResponder) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setShouldBecomeFirstResponder:NO]; [webView synchronouslyLoadTestPageNamed:@"selected-text-and-textarea"]; [simulator runFrom:CGPointMake(160, 100) to:CGPointMake(160, 300)]; EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"destination.value"]); } TEST(DragAndDropTests, CanDragImageWhenNotFirstResponder) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setShouldBecomeFirstResponder:NO]; [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 250)]; NSURL *droppedImageURL = [NSURL URLWithString:[webView stringByEvaluatingJavaScript:@"editor.querySelector('img').src"]]; EXPECT_WK_STREQ("blob", droppedImageURL.scheme); } TEST(DragAndDropTests, ContentEditableMoveParagraphs) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); loadTestPageAndEnsureInputSession(simulator.get(), @"two-paragraph-contenteditable"); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(250, 450)]; NSString *finalTextContent = [webView stringByEvaluatingJavaScript:@"editor.textContent"]; NSUInteger firstParagraphOffset = [finalTextContent rangeOfString:@"This is the first paragraph"].location; NSUInteger secondParagraphOffset = [finalTextContent rangeOfString:@"This is the second paragraph"].location; EXPECT_FALSE(firstParagraphOffset == NSNotFound); EXPECT_FALSE(secondParagraphOffset == NSNotFound); EXPECT_GT(firstParagraphOffset, secondParagraphOffset); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(251, 220, 2, 20), [simulator finalSelectionStartRect]); EXPECT_TRUE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, DragImageFromContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView synchronouslyLoadTestPageNamed:@"contenteditable-and-target"]; [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"]); } TEST(DragAndDropTests, TextAreaToInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); loadTestPageAndEnsureInputSession(simulator.get(), @"textarea-to-input"); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.value"].length, 0UL); EXPECT_WK_STREQ("Hello world", [webView editorValue].UTF8String); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(1033, 241, 2, 240), [simulator finalSelectionStartRect]); } TEST(DragAndDropTests, SinglePlainTextWordTypeIdentifiers) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); loadTestPageAndEnsureInputSession(simulator.get(), @"textarea-to-input"); [webView stringByEvaluatingJavaScript:@"source.value = 'pneumonoultramicroscopicsilicovolcanoconiosis'"]; [webView stringByEvaluatingJavaScript:@"source.selectionStart = 0"]; [webView stringByEvaluatingJavaScript:@"source.selectionEnd = source.value.length"]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; NSItemProvider *itemProvider = [simulator sourceItemProviders].firstObject; NSArray *registeredTypes = [itemProvider registeredTypeIdentifiers]; EXPECT_EQ(1UL, registeredTypes.count); EXPECT_WK_STREQ([(__bridge NSString *)kUTTypeUTF8PlainText UTF8String], [registeredTypes.firstObject UTF8String]); EXPECT_EQ([webView stringByEvaluatingJavaScript:@"source.value"].length, 0UL); EXPECT_EQ(UIPreferredPresentationStyleInline, itemProvider.preferredPresentationStyle); EXPECT_WK_STREQ("pneumonoultramicroscopicsilicovolcanoconiosis", [webView editorValue].UTF8String); } TEST(DragAndDropTests, SinglePlainTextURLTypeIdentifiers) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); loadTestPageAndEnsureInputSession(simulator.get(), @"textarea-to-input"); [webView stringByEvaluatingJavaScript:@"source.value = 'https://webkit.org/'"]; [webView stringByEvaluatingJavaScript:@"source.selectionStart = 0"]; [webView stringByEvaluatingJavaScript:@"source.selectionEnd = source.value.length"]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; NSItemProvider *itemProvider = [simulator sourceItemProviders].firstObject; NSArray *registeredTypes = [itemProvider registeredTypeIdentifiers]; EXPECT_EQ(2UL, registeredTypes.count); EXPECT_WK_STREQ([(__bridge NSString *)kUTTypeURL UTF8String], [registeredTypes.firstObject UTF8String]); EXPECT_WK_STREQ([(__bridge NSString *)kUTTypeUTF8PlainText UTF8String], [registeredTypes.lastObject UTF8String]); EXPECT_EQ(0UL, [webView stringByEvaluatingJavaScript:@"source.value"].length); EXPECT_EQ(UIPreferredPresentationStyleInline, itemProvider.preferredPresentationStyle); EXPECT_WK_STREQ("https://webkit.org/", [webView editorValue].UTF8String); } TEST(DragAndDropTests, LinkToInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"link-and-input"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String); __block bool doneLoadingURL = false; NSItemProvider *sourceItemProvider = [simulator sourceItemProviders].firstObject; [sourceItemProvider loadObjectOfClass:[NSURL class] completionHandler:^(id object, NSError *error) { NSURL *url = object; EXPECT_WK_STREQ("Hello world", url._title.UTF8String ?: ""); doneLoadingURL = true; }]; TestWebKitAPI::Util::run(&doneLoadingURL); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); EXPECT_TRUE([observedEventNames containsObject:@"drop"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(2069, 273, 2, 240), [simulator finalSelectionStartRect]); checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypeURL, 0); } TEST(DragAndDropTests, BackgroundImageLinkToInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"background-image-link-and-input"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("https://www.apple.com/", [webView editorValue].UTF8String); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); EXPECT_TRUE([observedEventNames containsObject:@"drop"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(2069, 241, 2, 240), [simulator finalSelectionStartRect]); checkTypeIdentifierIsRegisteredAtIndex(simulator.get(), (__bridge NSString *)kUTTypeURL, 0); } TEST(DragAndDropTests, CanPreventStart) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"prevent-start"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_EQ(DragAndDropPhaseCancelled, [simulator phase]); EXPECT_FALSE([webView editorContainsImageElement]); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_FALSE([observedEventNames containsObject:@"dragenter"]); EXPECT_FALSE([observedEventNames containsObject:@"dragover"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(0, 0, 0, 0), [simulator finalSelectionStartRect]); } TEST(DragAndDropTests, CanPreventOperation) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"prevent-operation"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_FALSE([webView editorContainsImageElement]); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(0, 0, 0, 0), [simulator finalSelectionStartRect]); } TEST(DragAndDropTests, EnterAndLeaveEvents) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"link-and-input"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 450)]; EXPECT_WK_STREQ("", [webView editorValue].UTF8String); NSArray *observedEventNames = [simulator observedEventNames]; EXPECT_TRUE([observedEventNames containsObject:@"dragenter"]); EXPECT_TRUE([observedEventNames containsObject:@"dragover"]); EXPECT_TRUE([observedEventNames containsObject:@"dragleave"]); EXPECT_FALSE([observedEventNames containsObject:@"drop"]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(0, 0, 0, 0), [simulator finalSelectionStartRect]); } TEST(DragAndDropTests, CanStartDragOnDivWithDraggableAttribute) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"custom-draggable-div"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 250)]; EXPECT_GT([simulator sourceItemProviders].count, 0UL); NSItemProvider *itemProvider = [simulator sourceItemProviders].firstObject; EXPECT_EQ(UIPreferredPresentationStyleInline, itemProvider.preferredPresentationStyle); EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!destination.querySelector('#item')"]); EXPECT_WK_STREQ(@"PASS", [webView stringByEvaluatingJavaScript:@"item.textContent"]); } TEST(DragAndDropTests, ExternalSourcePlainTextToIFrame) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"contenteditable-in-iframe"]; auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll]; auto simulator = adoptNS([[DragAndDropSimulator 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(DragAndDropTests, ExternalSourceInlineTextToFileInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulatedItemProvider = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline]; [simulatedItemProvider registerObject:@"This item provider requested inline presentation style." visibility:NSItemProviderRepresentationVisibilityAll]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)]; EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"output.value"]); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceJSONToFileInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulatedJSONItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *jsonData = [@"{ \"foo\": \"bar\", \"bar\": \"baz\" }" dataUsingEncoding:NSUTF8StringEncoding]; [simulatedJSONItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJSON withData:jsonData]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedJSONItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)]; EXPECT_WK_STREQ("application/json", [webView stringByEvaluatingJavaScript:@"output.value"]); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceImageToFileInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulatedImageItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5); [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG withData:imageData]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedImageItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("image/jpeg", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceHTMLToUploadArea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulatedHTMLItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *htmlData = [@"" dataUsingEncoding:NSUTF8StringEncoding]; [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:htmlData]; [simulatedHTMLItemProvider setSuggestedName:@"index.html"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setShouldAllowMoveOperation:NO]; [simulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("text/html", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceMoveOperationNotAllowed) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; [webView stringByEvaluatingJavaScript:@"upload.dropEffect = 'move'"]; auto simulatedHTMLItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *htmlData = [@"" dataUsingEncoding:NSUTF8StringEncoding]; [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:htmlData]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setShouldAllowMoveOperation:NO]; [simulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"output.value"]); } TEST(DragAndDropTests, ExternalSourcePKCS12ToSingleFileInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto item = adoptNS([[NSItemProvider alloc] init]); [item registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypePKCS12 withData:[@"Not a real p12 file." dataUsingEncoding:NSUTF8StringEncoding]]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ item.get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)]; EXPECT_WK_STREQ("application/x-pkcs12", [webView stringByEvaluatingJavaScript:@"output.value"]); } TEST(DragAndDropTests, ExternalSourceZIPArchiveAndURLToSingleFileInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto archiveProvider = adoptNS([[NSItemProvider alloc] init]); [archiveProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeZipArchive withData:testZIPArchive()]; auto urlProvider = adoptNS([[NSItemProvider alloc] init]); [urlProvider registerObject:[NSURL URLWithString:@"https://webkit.org"] visibility:NSItemProviderRepresentationVisibilityAll]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ archiveProvider.get(), urlProvider.get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("application/zip", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceZIPArchiveToUploadArea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeZipArchive withData:testZIPArchive()]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("application/zip", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceImageAndHTMLToSingleFileInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulatedImageItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5); [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG withData:imageData]; auto simulatedHTMLItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *htmlData = [@"" dataUsingEncoding:NSUTF8StringEncoding]; [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:htmlData]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get(), simulatedImageItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceImageAndHTMLToMultipleFileInput) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; [webView stringByEvaluatingJavaScript:@"input.setAttribute('multiple', '')"]; auto simulatedImageItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5); [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG withData:imageData]; auto simulatedHTMLItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *htmlData = [@"" dataUsingEncoding:NSUTF8StringEncoding]; [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:htmlData]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedHTMLItemProvider.get(), simulatedImageItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 100)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("image/jpeg, text/html", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceImageAndHTMLToUploadArea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulatedImageItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5); [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG withData:imageData]; auto firstSimulatedHTMLItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *firstHTMLData = [@"" dataUsingEncoding:NSUTF8StringEncoding]; [firstSimulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:firstHTMLData]; auto secondSimulatedHTMLItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *secondHTMLData = [@"hello world" dataUsingEncoding:NSUTF8StringEncoding]; [secondSimulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:secondHTMLData]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setShouldAllowMoveOperation:NO]; [simulator setExternalItemProviders:@[ simulatedImageItemProvider.get(), firstSimulatedHTMLItemProvider.get(), secondSimulatedHTMLItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("image/jpeg, text/html, text/html", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } TEST(DragAndDropTests, ExternalSourceHTMLToContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *htmlData = [@"

This is a test

" dataUsingEncoding:NSUTF8StringEncoding]; [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:htmlData]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; NSString *textContent = [webView stringByEvaluatingJavaScript:@"editor.textContent"]; EXPECT_WK_STREQ("This is a test", textContent.UTF8String); EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"!!editor.querySelector('h1')"].boolValue); } TEST(DragAndDropTests, ExternalSourceBoldSystemAttributedStringToContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); NSDictionary *textAttributes = @{ NSFontAttributeName: [UIFont boldSystemFontOfSize:20] }; auto richText = adoptNS([[NSAttributedString alloc] initWithString:@"This is a test" attributes:textAttributes]); auto itemProvider = adoptNS([[NSItemProvider alloc] initWithObject:richText.get()]); [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("This is a test", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String); } TEST(DragAndDropTests, ExternalSourceColoredAttributedStringToContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); NSDictionary *textAttributes = @{ NSForegroundColorAttributeName: [UIColor redColor] }; auto richText = adoptNS([[NSAttributedString alloc] initWithString:@"This is a test" attributes:textAttributes]); auto itemProvider = adoptNS([[NSItemProvider alloc] initWithObject:richText.get()]); [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("rgb(255, 0, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(document.querySelector('p')).color"]); EXPECT_WK_STREQ("This is a test", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String); } TEST(DragAndDropTests, ExternalSourceMultipleURLsToContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto firstItem = adoptNS([[NSItemProvider alloc] init]); [firstItem registerObject:[NSURL URLWithString:@"https://www.apple.com/iphone/"] visibility:NSItemProviderRepresentationVisibilityAll]; auto secondItem = adoptNS([[NSItemProvider alloc] init]); [secondItem registerObject:[NSURL URLWithString:@"https://www.apple.com/mac/"] visibility:NSItemProviderRepresentationVisibilityAll]; auto thirdItem = adoptNS([[NSItemProvider alloc] init]); [thirdItem registerObject:[NSURL URLWithString:@"https://webkit.org/"] visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ firstItem.get(), secondItem.get(), thirdItem.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; NSArray *droppedURLs = [webView objectByEvaluatingJavaScript:@"Array.from(editor.querySelectorAll('a')).map(a => a.href)"]; EXPECT_EQ(3UL, droppedURLs.count); EXPECT_WK_STREQ("https://www.apple.com/iphone/", droppedURLs[0]); EXPECT_WK_STREQ("https://www.apple.com/mac/", droppedURLs[1]); EXPECT_WK_STREQ("https://webkit.org/", droppedURLs[2]); NSArray *linksSeparatedByLine = [[webView objectByEvaluatingJavaScript:@"editor.innerText"] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; EXPECT_EQ(3UL, linksSeparatedByLine.count); EXPECT_WK_STREQ("https://www.apple.com/iphone/", linksSeparatedByLine[0]); EXPECT_WK_STREQ("https://www.apple.com/mac/", linksSeparatedByLine[1]); EXPECT_WK_STREQ("https://webkit.org/", linksSeparatedByLine[2]); } TEST(DragAndDropTests, RespectsExternalSourceFidelityRankings) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); // Here, our source item provider vends two representations: plain text, and then an image. If we don't respect the // fidelity order requested by the source, we'll end up assuming that the image is a higher fidelity representation // than the plain text, and erroneously insert the image. If we respect source fidelities, we'll insert text rather // than an image. auto simulatedItemProviderWithTextFirst = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProviderWithTextFirst registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll]; [simulatedItemProviderWithTextFirst registerObject:testIconImage() visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ simulatedItemProviderWithTextFirst.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"]); EXPECT_FALSE([webView editorContainsImageElement]); [webView stringByEvaluatingJavaScript:@"editor.innerHTML = ''"]; // Now we register the item providers in reverse, and expect the image to be inserted instead of text. auto simulatedItemProviderWithImageFirst = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProviderWithImageFirst registerObject:testIconImage() visibility:NSItemProviderRepresentationVisibilityAll]; [simulatedItemProviderWithImageFirst registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ simulatedItemProviderWithImageFirst.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"editor.textContent"]); EXPECT_TRUE([webView editorContainsImageElement]); } static BOOL overrideIsInHardwareKeyboardMode() { return NO; } TEST(DragAndDropTests, ExternalSourceUTF8PlainTextOnly) { ClassMethodSwizzler swizzler([UIKeyboard class], @selector(isInHardwareKeyboardMode), reinterpret_cast(overrideIsInHardwareKeyboardMode)); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; NSString *textPayload = @"Ceci n'est pas une string"; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto simulatedItemProvider = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionBlock) { completionBlock([textPayload dataUsingEncoding:NSUTF8StringEncoding], nil); return [NSProgress discreteProgressWithTotalUnitCount:100]; }]; [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ(textPayload.UTF8String, [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(1935, 205, 2, 223), [simulator finalSelectionStartRect]); } TEST(DragAndDropTests, ExternalSourceJPEGOnly) { ClassMethodSwizzler swizzler([UIKeyboard class], @selector(isInHardwareKeyboardMode), reinterpret_cast(overrideIsInHardwareKeyboardMode)); auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto simulatedItemProvider = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionBlock) { completionBlock(UIImageJPEGRepresentation(testIconImage(), 0.5), nil); return [NSProgress discreteProgressWithTotalUnitCount:100]; }]; [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_TRUE([webView editorContainsImageElement]); checkCGRectIsEqualToCGRectWithLogging(CGRectMake(214, 201, 2, 223), [simulator finalSelectionStartRect]); } TEST(DragAndDropTests, ExternalSourceTitledNSURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; NSURL *titledURL = [NSURL URLWithString:@"https://www.apple.com"]; titledURL._title = @"Apple"; auto simulatedItemProvider = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProvider registerObject:titledURL visibility:NSItemProviderRepresentationVisibilityAll]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("Apple", [webView stringByEvaluatingJavaScript:@"editor.querySelector('a').textContent"]); EXPECT_WK_STREQ("https://www.apple.com/", [webView stringByEvaluatingJavaScript:@"editor.querySelector('a').href"]); } TEST(DragAndDropTests, ExternalSourceFileURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; NSURL *URL = [NSURL URLWithString:@"file:///some/file/that/is/not/real"]; NSItemProvider *simulatedItemProvider = [NSItemProvider itemProviderWithURL:URL title:@""]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedItemProvider ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_FALSE([[webView stringByEvaluatingJavaScript:@"!!editor.querySelector('a')"] boolValue]); EXPECT_WK_STREQ("Hello world\nfile:///some/file/that/is/not/real", [webView stringByEvaluatingJavaScript:@"document.body.innerText"]); } TEST(DragAndDropTests, ExternalSourceOverrideDropFileUpload) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulatedImageItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *imageData = UIImageJPEGRepresentation(testIconImage(), 0.5); [simulatedImageItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG withData:imageData]; auto simulatedHTMLItemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *firstHTMLData = [@"" dataUsingEncoding:NSUTF8StringEncoding]; [simulatedHTMLItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:firstHTMLData]; [simulatedHTMLItemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setOverridePerformDropBlock:^NSArray *(id session) { EXPECT_EQ(2UL, session.items.count); UIDragItem *firstItem = session.items[0]; UIDragItem *secondItem = session.items[1]; EXPECT_TRUE([firstItem.itemProvider.registeredTypeIdentifiers isEqual:@[ (__bridge NSString *)kUTTypeJPEG ]]); EXPECT_TRUE([secondItem.itemProvider.registeredTypeIdentifiers isEqual:@[ (__bridge NSString *)kUTTypeHTML ]]); return @[ secondItem ]; }]; [simulator setExternalItemProviders:@[ simulatedImageItemProvider.get(), simulatedHTMLItemProvider.get() ]]; [simulator runFrom:CGPointMake(200, 300) to:CGPointMake(100, 300)]; NSString *outputValue = [webView stringByEvaluatingJavaScript:@"output.value"]; EXPECT_WK_STREQ("text/html", outputValue.UTF8String); EXPECT_FALSE([simulator lastKnownDropProposal].precise); } static RetainPtr createMapItemForTesting() { auto placemark = adoptNS([allocMKPlacemarkInstance() initWithCoordinate:CLLocationCoordinate2DMake(37.3327, -122.0053)]); auto item = adoptNS([allocMKMapItemInstance() initWithPlacemark:placemark.get()]); [item setName:@"Apple Park"]; auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerObject:item.get() visibility:NSItemProviderRepresentationVisibilityAll]; [itemProvider setSuggestedName:[item name]]; return itemProvider; } static RetainPtr createContactItemForTesting() { auto contact = adoptNS([allocCNMutableContactInstance() init]); [contact setGivenName:@"Foo"]; [contact setFamilyName:@"Bar"]; auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerObject:contact.get() visibility:NSItemProviderRepresentationVisibilityAll]; [itemProvider setSuggestedName:@"Foo Bar"]; return itemProvider; } TEST(DragAndDropTests, ExternalSourceMapItemIntoEditableAreas) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"contenteditable-and-textarea"]; [webView _synchronouslyExecuteEditCommand:@"Delete" argument:nil]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ createMapItemForTesting().get() ]]; [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(100, 100)]; EXPECT_WK_STREQ("Apple Park", [webView stringByEvaluatingJavaScript:@"document.querySelector('div[contenteditable]').textContent"]); NSURL *firstURL = [NSURL URLWithString:[webView stringByEvaluatingJavaScript:@"document.querySelector('a').href"]]; EXPECT_WK_STREQ("maps.apple.com", firstURL.host); [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(100, 300)]; NSURL *secondURL = [NSURL URLWithString:[webView stringByEvaluatingJavaScript:@"document.querySelector('textarea').value"]]; EXPECT_WK_STREQ("maps.apple.com", secondURL.host); } TEST(DragAndDropTests, ExternalSourceContactIntoEditableAreas) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"contenteditable-and-textarea"]; [webView _synchronouslyExecuteEditCommand:@"Delete" argument:nil]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ createContactItemForTesting().get() ]]; [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(100, 100)]; EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"document.querySelector('div[contenteditable]').textContent"]); [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"document.querySelector('textarea').textContent"]); } TEST(DragAndDropTests, ExternalSourceMapItemAndContactToUploadArea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"file-uploading"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ createMapItemForTesting().get(), createContactItemForTesting().get() ]]; [simulator runFrom:CGPointMake(200, 100) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("text/vcard, text/vcard", [webView stringByEvaluatingJavaScript:@"output.value"]); } static RetainPtr setUpTestWebViewForDataTransferItems() { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"DataTransferItem-getAsEntry"]; auto preferences = (__bridge WKPreferencesRef)[[webView configuration] preferences]; WKPreferencesSetDataTransferItemsEnabled(preferences, true); WKPreferencesSetDirectoryUploadEnabled(preferences, true); return webView; } TEST(DragAndDropTests, ExternalSourceDataTransferItemGetFolderAsEntry) { // The expected output is sorted by alphabetical order here for consistent behavior across different test environments. // See DataTransferItem-getAsEntry.html for more details. NSArray *expectedOutput = @[ @"Found data transfer item (kind: 'file', type: '')", @"DIR: /somedirectory", @"DIR: /somedirectory/subdirectory1", @"DIR: /somedirectory/subdirectory2", [NSString stringWithFormat:@"FILE: /somedirectory/archive.zip ('application/zip', %tu bytes)", testZIPArchive().length], [NSString stringWithFormat:@"FILE: /somedirectory/icon.png ('image/png', %tu bytes)", testIconImageData().length], @"FILE: /somedirectory/subdirectory1/text-file-1.txt ('text/plain', 43 bytes)", @"FILE: /somedirectory/subdirectory2/text-file-2.txt ('text/plain', 44 bytes)" ]; auto webView = setUpTestWebViewForDataTransferItems(); __block bool done = false; [webView performAfterReceivingMessage:@"dropped" action:^() { done = true; }]; runTestWithTemporaryFolder(^(NSURL *folderURL) { auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider setSuggestedName:@"somedirectory"]; [itemProvider registerFileRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeFolder fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[capturedFolderURL = retainPtr(folderURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * { completionHandler(capturedFolderURL.get(), NO, nil); return nil; }]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(150, 50)]; }); TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ([expectedOutput componentsJoinedByString:@"\n"], [webView stringByEvaluatingJavaScript:@"output.value"]); } TEST(DragAndDropTests, ExternalSourceDataTransferItemGetPlainTextFileAsEntry) { NSArray *expectedOutput = @[ @"Found data transfer item (kind: 'file', type: 'text/plain')", @"FILE: /foo.txt ('text/plain', 28 bytes)" ]; auto webView = setUpTestWebViewForDataTransferItems(); __block bool done = false; [webView performAfterReceivingMessage:@"dropped" action:^() { done = true; }]; runTestWithTemporaryTextFile(^(NSURL *fileURL) { auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider setSuggestedName:@"foo"]; [itemProvider registerFileRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[capturedFileURL = retainPtr(fileURL)](FileLoadCompletionBlock completionHandler) -> NSProgress * { completionHandler(capturedFileURL.get(), NO, nil); return nil; }]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(150, 50)]; }); TestWebKitAPI::Util::run(&done); EXPECT_WK_STREQ([expectedOutput componentsJoinedByString:@"\n"], [webView stringByEvaluatingJavaScript:@"output.value"]); } TEST(DragAndDropTests, ExternalSourceOverrideDropInsertURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setOverridePerformDropBlock:^NSArray *(id session) { NSMutableArray *allowedItems = [NSMutableArray array]; for (UIDragItem *item in session.items) { if ([item.itemProvider.registeredTypeIdentifiers containsObject:(__bridge NSString *)kUTTypeURL]) [allowedItems addObject:item]; } EXPECT_EQ(1UL, allowedItems.count); return allowedItems; }]; auto firstItemProvider = adoptNS([[NSItemProvider alloc] init]); [firstItemProvider registerObject:@"This is a string." visibility:NSItemProviderRepresentationVisibilityAll]; auto secondItemProvider = adoptNS([[NSItemProvider alloc] init]); [secondItemProvider registerObject:[NSURL URLWithString:@"https://webkit.org/"] visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ firstItemProvider.get(), secondItemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("https://webkit.org/", [webView stringByEvaluatingJavaScript:@"editor.textContent"]); } TEST(DragAndDropTests, OverrideDrop) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"simple"]; auto simulatedItemProvider = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:[@"" dataUsingEncoding:NSUTF8StringEncoding]]; __block bool finishedLoadingData = false; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; [simulator setOverrideDragUpdateBlock:[] (UIDropOperation operation, id session) { EXPECT_EQ(UIDropOperationCancel, operation); return UIDropOperationCopy; }]; [simulator setDropCompletionBlock:^(BOOL handled, NSArray *itemProviders) { EXPECT_FALSE(handled); [itemProviders.firstObject loadDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML completionHandler:^(NSData *data, NSError *error) { auto text = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); EXPECT_WK_STREQ("", [text UTF8String]); EXPECT_FALSE(!!error); finishedLoadingData = true; }]; }]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; TestWebKitAPI::Util::run(&finishedLoadingData); } TEST(DragAndDropTests, InjectedBundleOverridePerformTwoStepDrop) { WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]; [configuration.processPool _setObject:@YES forBundleParameter:@"BundleOverridePerformTwoStepDrop"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]); [webView loadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulatedItemProvider = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionBlock) { completionBlock([@"Hello world" dataUsingEncoding:NSUTF8StringEncoding], nil); return [NSProgress discreteProgressWithTotalUnitCount:100]; }]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_EQ(0UL, [webView stringByEvaluatingJavaScript:@"editor.textContent"].length); } TEST(DragAndDropTests, InjectedBundleAllowPerformTwoStepDrop) { WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]; [configuration.processPool _setObject:@NO forBundleParameter:@"BundleOverridePerformTwoStepDrop"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]); [webView synchronouslyLoadTestPageNamed:@"autofocus-contenteditable"]; [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"]; auto simulatedItemProvider = adoptNS([[NSItemProvider alloc] init]); [simulatedItemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionBlock) { completionBlock([@"Hello world" dataUsingEncoding:NSUTF8StringEncoding], nil); return [NSProgress discreteProgressWithTotalUnitCount:100]; }]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ simulatedItemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 400) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("Hello world", [webView stringByEvaluatingJavaScript:@"editor.textContent"].UTF8String); } TEST(DragAndDropTests, InjectedBundleImageElementData) { WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]; [configuration _setAttachmentElementEnabled:YES]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]); [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"]; __block RetainPtr injectedString; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setConvertItemProvidersBlock:^NSArray *(NSItemProvider *itemProvider, NSArray *, NSDictionary *data) { injectedString = adoptNS([[NSString alloc] initWithData:data[InjectedBundlePasteboardDataType] encoding:NSUTF8StringEncoding]); return @[ itemProvider ]; }]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 250)]; EXPECT_WK_STREQ("hello", [injectedString UTF8String]); } TEST(DragAndDropTests, InjectedBundleAttachmentElementData) { WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]; [configuration _setAttachmentElementEnabled:YES]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration]); [webView synchronouslyLoadTestPageNamed:@"attachment-element"]; __block RetainPtr injectedString; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setConvertItemProvidersBlock:^NSArray *(NSItemProvider *itemProvider, NSArray *, NSDictionary *data) { injectedString = adoptNS([[NSString alloc] initWithData:data[InjectedBundlePasteboardDataType] encoding:NSUTF8StringEncoding]); return @[ itemProvider ]; }]; [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 400)]; EXPECT_WK_STREQ("hello", [injectedString UTF8String]); EXPECT_TRUE([webView stringByEvaluatingJavaScript:@"getSelection().isCollapsed"].boolValue); } TEST(DragAndDropTests, LargeImageToTargetDiv) { auto testWebViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); [[testWebViewConfiguration preferences] _setLargeImageAsyncDecodingEnabled:NO]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:testWebViewConfiguration.get()]); [webView synchronouslyLoadTestPageNamed:@"div-and-large-image"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(200, 400) to:CGPointMake(200, 150)]; EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String); checkFirstTypeIsPresentAndSecondTypeIsMissing(simulator.get(), kUTTypePNG, kUTTypeFileURL); checkEstimatedSize(simulator.get(), { 2000, 2000 }); } TEST(DragAndDropTests, LinkWithEmptyHREF) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"link-and-input"]; [webView stringByEvaluatingJavaScript:@"document.querySelector('a').href = ''"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_EQ(DragAndDropPhaseCancelled, [simulator phase]); EXPECT_WK_STREQ("", [webView editorValue].UTF8String); } TEST(DragAndDropTests, CancelledLiftDoesNotCauseSubsequentDragsToFail) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"link-and-target-div"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setConvertItemProvidersBlock:^NSArray *(NSItemProvider *, NSArray *, NSDictionary *) { return @[ ]; }]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_EQ(DragAndDropPhaseCancelled, [simulator phase]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"target.textContent"]); NSString *outputText = [webView stringByEvaluatingJavaScript:@"output.textContent"]; checkStringArraysAreEqual(@[@"dragstart", @"dragend"], [outputText componentsSeparatedByString:@" "]); [webView stringByEvaluatingJavaScript:@"output.innerHTML = ''"]; [simulator setConvertItemProvidersBlock:^NSArray *(NSItemProvider *itemProvider, NSArray *, NSDictionary *) { return @[ itemProvider ]; }]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"]); [webView stringByEvaluatingJavaScript:@"output.textContent"]; checkStringArraysAreEqual(@[@"dragstart", @"dragend"], [outputText componentsSeparatedByString:@" "]); } TEST(DragAndDropTests, WebProcessTerminationDuringDrag) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"link-and-target-div"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setSessionWillBeginBlock:^{ [webView _killWebContentProcessAndResetState]; }]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(300, 50)]; } TEST(DragAndDropTests, WebViewRemovedFromViewHierarchyDuringDrag) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"link-and-target-div"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setConvertItemProvidersBlock:[webView] (NSItemProvider *item, NSArray *, NSDictionary *) -> NSArray * { [webView removeFromSuperview]; return @[ item ]; }]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(300, 50)]; EXPECT_EQ([simulator cancellationPreviews].firstObject, nil); } static void testDragAndDropOntoTargetElements(TestWKWebView *webView) { auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView]); [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 250)]; EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target1).backgroundColor"]); EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target1.textContent"]); [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(250, 50)]; EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target2).backgroundColor"]); EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target2.textContent"]); [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(250, 250)]; EXPECT_WK_STREQ("rgb(0, 128, 0)", [webView stringByEvaluatingJavaScript:@"getComputedStyle(target3).backgroundColor"]); EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target3.textContent"]); } TEST(DragAndDropTests, DragEventClientCoordinatesBasic) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"drop-targets"]; testDragAndDropOntoTargetElements(webView.get()); } TEST(DragAndDropTests, DragEventClientCoordinatesWithScrollOffset) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"drop-targets"]; [webView stringByEvaluatingJavaScript:@"document.body.style.margin = '1000px 0'"]; [webView stringByEvaluatingJavaScript:@"document.scrollingElement.scrollTop = 1000"]; [webView waitForNextPresentationUpdate]; testDragAndDropOntoTargetElements(webView.get()); } TEST(DragAndDropTests, DragEventPageCoordinatesBasic) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"drop-targets"]; [webView stringByEvaluatingJavaScript:@"window.usePageCoordinates = true"]; testDragAndDropOntoTargetElements(webView.get()); } TEST(DragAndDropTests, DragEventPageCoordinatesWithScrollOffset) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"drop-targets"]; [webView stringByEvaluatingJavaScript:@"document.body.style.margin = '1000px 0'"]; [webView stringByEvaluatingJavaScript:@"document.scrollingElement.scrollTop = 1000"]; [webView stringByEvaluatingJavaScript:@"window.usePageCoordinates = true"]; [webView waitForNextPresentationUpdate]; testDragAndDropOntoTargetElements(webView.get()); } TEST(DragAndDropTests, DoNotCrashWhenSelectionIsClearedInDragStart) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dragstart-clear-selection"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 100)]; EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"paragraph.textContent"]); } // FIXME: Re-enable this test once we resolve #if 0 TEST(DragAndDropTests, CustomActionSheetPopover) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"link-and-target-div"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setShouldEnsureUIApplication:YES]; __block BOOL didInvokeCustomActionSheet = NO; [simulator setShowCustomActionSheetBlock:^BOOL(_WKActivatedElementInfo *element) { EXPECT_EQ(_WKActivatedElementTypeLink, element.type); EXPECT_WK_STREQ("Hello world", element.title.UTF8String); didInvokeCustomActionSheet = YES; return YES; }]; [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; EXPECT_TRUE(didInvokeCustomActionSheet); EXPECT_WK_STREQ("PASS", [webView stringByEvaluatingJavaScript:@"target.textContent"].UTF8String); } #endif TEST(DragAndDropTests, UnresponsivePageDoesNotHangUI) { auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]); [processPoolConfiguration setIgnoreSynchronousMessagingTimeoutsForTesting:YES]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get() processPoolConfiguration:processPoolConfiguration.get()]); [webView synchronouslyLoadTestPageNamed:@"simple"]; [webView evaluateJavaScript:@"while(1);" completionHandler:nil]; // The test passes if we can prepare for a drag session without timing out. auto dragSession = adoptNS([[MockDragSession alloc] init]); [(id )[webView dragInteractionDelegate] _dragInteraction:[webView dragInteraction] prepareForSession:dragSession.get() completion:^() { }]; } TEST(DragAndDropTests, WebItemProviderPasteboardLoading) { static NSString *fastString = @"This data loads quickly"; static NSString *slowString = @"This data loads slowly"; WebItemProviderPasteboard *pasteboard = [WebItemProviderPasteboard sharedInstance]; auto fastItem = adoptNS([[NSItemProvider alloc] init]); [fastItem registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionBlock) { completionBlock([fastString dataUsingEncoding:NSUTF8StringEncoding], nil); return nil; }]; auto slowItem = adoptNS([[NSItemProvider alloc] init]); [slowItem registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionBlock) { sleep(2_s); completionBlock([slowString dataUsingEncoding:NSUTF8StringEncoding], nil); return nil; }]; __block bool hasRunFirstCompletionBlock = false; pasteboard.itemProviders = @[ fastItem.get(), slowItem.get() ]; [pasteboard doAfterLoadingProvidedContentIntoFileURLs:^(NSArray *urls) { EXPECT_EQ(2UL, urls.count); auto firstString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[0] encoding:NSUTF8StringEncoding error:nil]); auto secondString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[1] encoding:NSUTF8StringEncoding error:nil]); EXPECT_WK_STREQ(fastString, [firstString UTF8String]); EXPECT_WK_STREQ(slowString, [secondString UTF8String]); hasRunFirstCompletionBlock = true; } synchronousTimeout:600]; EXPECT_TRUE(hasRunFirstCompletionBlock); __block bool hasRunSecondCompletionBlock = false; [pasteboard doAfterLoadingProvidedContentIntoFileURLs:^(NSArray *urls) { EXPECT_EQ(2UL, urls.count); auto firstString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[0] encoding:NSUTF8StringEncoding error:nil]); auto secondString = adoptNS([[NSString alloc] initWithContentsOfURL:urls[1] encoding:NSUTF8StringEncoding error:nil]); EXPECT_WK_STREQ(fastString, [firstString UTF8String]); EXPECT_WK_STREQ(slowString, [secondString UTF8String]); hasRunSecondCompletionBlock = true; } synchronousTimeout:0]; EXPECT_FALSE(hasRunSecondCompletionBlock); TestWebKitAPI::Util::run(&hasRunSecondCompletionBlock); } TEST(DragAndDropTests, DoNotCrashWhenSelectionMovesOffscreenAfterDragStart) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dragstart-change-selection-offscreen"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 100)]; EXPECT_WK_STREQ("FAR OFFSCREEN", [webView stringByEvaluatingJavaScript:@"getSelection().getRangeAt(0).toString()"]); } TEST(DragAndDropTests, AdditionalItemsCanBePreventedOnDragStart) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"selected-text-image-link-and-editable"]; [webView stringByEvaluatingJavaScript:@"link.addEventListener('dragstart', e => e.preventDefault())"]; [webView stringByEvaluatingJavaScript:@"image.addEventListener('dragstart', e => e.preventDefault())"]; auto simulator = adoptNS([[DragAndDropSimulator 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", [webView stringByEvaluatingJavaScript:@"editor.textContent"]); } TEST(DragAndDropTests, AdditionalLinkAndImageIntoContentEditable) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"selected-text-image-link-and-editable"]; auto simulator = adoptNS([[DragAndDropSimulator 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("ABCDA 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"]); } TEST(DragAndDropTests, DragLiftPreviewDataTransferSetDragImage) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"DataTransfer-setDragImage"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); // Use DataTransfer.setDragImage to specify an existing image element in the DOM. [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(250, 50)]; checkCGRectIsEqualToCGRectWithLogging({{100, 50}, {215, 174}}, [simulator liftPreviews][0].view.frame); // Use DataTransfer.setDragImage to specify an existing image element in the DOM, with x and y offsets. [simulator runFrom:CGPointMake(10, 150) to:CGPointMake(250, 150)]; checkCGRectIsEqualToCGRectWithLogging({{-90, 50}, {215, 174}}, [simulator liftPreviews][0].view.frame); // Use DataTransfer.setDragImage to specify a newly created Image, disconnected from the DOM. [simulator runFrom:CGPointMake(100, 250) to:CGPointMake(250, 250)]; checkCGRectIsEqualToCGRectWithLogging({{100, 250}, {215, 174}}, [simulator liftPreviews][0].view.frame); // Don't use DataTransfer.setDragImage and fall back to snapshotting the dragged element. [simulator runFrom:CGPointMake(50, 350) to:CGPointMake(250, 350)]; checkCGRectIsEqualToCGRectWithLogging({{0, 300}, {300, 100}}, [simulator liftPreviews][0].view.frame); // Perform a normal drag on an image. [simulator runFrom:CGPointMake(50, 450) to:CGPointMake(250, 450)]; checkCGRectIsEqualToCGRectWithLogging({{0, 400}, {215, 174}}, [simulator liftPreviews][0].view.frame); } static NSData *testIconImageData() { return [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png" inDirectory:@"TestWebKitAPI.resources"]]; } TEST(DragAndDropTests, DataTransferGetDataWhenDroppingImageAndMarkup) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); WKPreferencesSetCustomPasteboardDataEnabled((__bridge WKPreferencesRef)[webView configuration].preferences, true); [webView synchronouslyLoadTestPageNamed:@"DataTransfer"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypePNG withData:testIconImageData()]; NSString *markupString = @"HELLO WORLD"; [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML withData:[markupString dataUsingEncoding:NSUTF8StringEncoding]]; [itemProvider setSuggestedName:@"icon"]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointZero to:CGPointMake(50, 100)]; EXPECT_WK_STREQ("Files, text/html", [webView stringByEvaluatingJavaScript:@"types.textContent"]); EXPECT_WK_STREQ("(STRING, text/html), (FILE, image/png)", [webView stringByEvaluatingJavaScript:@"items.textContent"]); EXPECT_WK_STREQ("('icon.png', image/png)", [webView stringByEvaluatingJavaScript:@"files.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"textData.textContent"]); EXPECT_WK_STREQ("HELLO WORLD", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]); EXPECT_FALSE([[webView stringByEvaluatingJavaScript:@"rawHTMLData.textContent"] containsString:@"script"]); } TEST(DragAndDropTests, DataTransferGetDataWhenDroppingPlainText) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"select(plain)"]; [simulator runFrom:CGPointMake(50, 75) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/plain" : @"" }, @"drop": @{ @"text/plain" : @"Plain text" } }); } TEST(DragAndDropTests, DataTransferGetDataWhenDroppingCustomData) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"select(rich)"]; [webView stringByEvaluatingJavaScript:@"writeCustomData = true"]; [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover" : @{ @"text/plain" : @"", @"foo/plain" : @"", @"text/html" : @"", @"bar/html" : @"", @"text/uri-list" : @"", @"baz/uri-list" : @"" }, @"drop" : @{ @"text/plain" : @"ben bitdiddle", @"foo/plain" : @"eva lu ator", @"text/html" : @"bold text", @"bar/html" : @"italic text", @"text/uri-list" : @"https://www.apple.com", @"baz/uri-list" : @"https://www.webkit.org" } }); } TEST(DragAndDropTests, DataTransferGetDataWhenDroppingURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"rich.innerHTML = 'This is a link.'"]; [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/uri-list" : @"", @"text/plain" : @"" }, @"drop": @{ @"text/uri-list" : @"https://www.apple.com/", @"text/plain" : @"https://www.apple.com/" } }); } TEST(DragAndDropTests, DataTransferGetDataWhenDroppingImageWithFileURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); NSURL *iconURL = [[NSBundle mainBundle] URLForResource:@"icon" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"]; [itemProvider registerFileRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypePNG fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedIconURL = retainPtr(iconURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * { completionHandler(protectedIconURL.get(), NO, nil); return nil; }]; [itemProvider registerObject:iconURL visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)]; // File URLs should never be exposed directly to web content, so DataTransfer.getData should return an empty string here. checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"Files": @"" }, @"drop": @{ @"Files": @"" } }); } TEST(DragAndDropTests, DataTransferGetDataWhenDroppingImageWithHTTPURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeJPEG visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler) { completionHandler(UIImageJPEGRepresentation(testIconImage(), 0.5), nil); return nil; }]; [itemProvider registerObject:[NSURL URLWithString:@"http://webkit.org"] visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"Files": @"", @"text/uri-list": @"" }, @"drop": @{ @"Files": @"", @"text/uri-list": @"http://webkit.org/" } }); } TEST(DragAndDropTests, DataTransferGetDataWhenDroppingRespectsPresentationStyle) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); runTestWithTemporaryTextFile(^(NSURL *fileURL) { auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerFileRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeUTF8PlainText fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedFileURL = retainPtr(fileURL)] (FileLoadCompletionBlock completionHandler) -> NSProgress * { completionHandler(protectedFileURL.get(), NO, nil); return nil; }]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment]; [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"Files" : @"" }, @"drop": @{ @"Files" : @"" } }); [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline]; [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/plain" : @"" }, @"drop": @{ @"text/plain" : @"This is a tiny blob of text." } }); }); } TEST(DragAndDropTests, DataTransferSetDataCannotWritePlatformTypes) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"select(rich)"]; [webView stringByEvaluatingJavaScript:@"customData = { 'text/plain' : 'foo', 'com.adobe.pdf' : 'try and decode me!' }"]; [webView stringByEvaluatingJavaScript:@"writeCustomData = true"]; [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; checkFirstTypeIsPresentAndSecondTypeIsMissing(simulator.get(), kUTTypeUTF8PlainText, kUTTypePDF); checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/plain": @"", @"com.adobe.pdf": @"" }, @"drop": @{ @"text/plain": @"foo", @"com.adobe.pdf": @"try and decode me!" } }); } TEST(DragAndDropTests, DataTransferGetDataReadPlainAndRichText) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"DataTransfer"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); NSDictionary *textAttributes = @{ NSFontAttributeName: [UIFont boldSystemFontOfSize:20] }; auto richText = adoptNS([[NSAttributedString alloc] initWithString:@"WebKit" attributes:textAttributes]); [itemProvider registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll]; [itemProvider registerObject:[NSURL URLWithString:@"https://www.webkit.org/"] visibility:NSItemProviderRepresentationVisibilityAll]; [itemProvider registerObject:@"WebKit" visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointZero to:CGPointMake(50, 100)]; EXPECT_WK_STREQ("text/html, text/plain, text/uri-list", [webView stringByEvaluatingJavaScript:@"types.textContent"]); EXPECT_WK_STREQ("WebKit", [webView stringByEvaluatingJavaScript:@"textData.textContent"]); EXPECT_WK_STREQ("https://www.webkit.org/", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]); EXPECT_WK_STREQ("WebKit", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]); EXPECT_WK_STREQ("(STRING, text/html), (STRING, text/plain), (STRING, text/uri-list)", [webView stringByEvaluatingJavaScript:@"items.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"files.textContent"]); } TEST(DragAndDropTests, DataTransferSuppressGetDataDueToPresenceOfTextFile) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"DataTransfer"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll]; [itemProvider setSuggestedName:@"hello.txt"]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointZero to:CGPointMake(50, 100)]; EXPECT_WK_STREQ("Files", [webView stringByEvaluatingJavaScript:@"types.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"textData.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]); EXPECT_WK_STREQ("(FILE, text/plain)", [webView stringByEvaluatingJavaScript:@"items.textContent"]); EXPECT_WK_STREQ("('hello.txt', text/plain)", [webView stringByEvaluatingJavaScript:@"files.textContent"]); } TEST(DragAndDropTests, DataTransferExposePlainTextWithFileURLAsFile) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"DataTransfer"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); NSData *urlAsData = [@"file:///some/file/path.txt" dataUsingEncoding:NSUTF8StringEncoding]; [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeFileURL withData:urlAsData]; [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeURL withData:urlAsData]; [itemProvider registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [simulator runFrom:CGPointZero to:CGPointMake(50, 100)]; EXPECT_WK_STREQ("Files", [webView stringByEvaluatingJavaScript:@"types.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"textData.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"urlData.textContent"]); EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"htmlData.textContent"]); EXPECT_WK_STREQ("(FILE, text/plain)", [webView stringByEvaluatingJavaScript:@"items.textContent"]); NSString *filesOutput = [webView stringByEvaluatingJavaScript:@"files.textContent"]; EXPECT_TRUE([filesOutput containsString:@"text/plain)"]); EXPECT_TRUE([filesOutput containsString:@".txt', "]); } TEST(DragAndDropTests, DataTransferGetDataCannotReadPrivateArbitraryTypes) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); auto itemProvider = adoptNS([[NSItemProvider alloc] init]); [itemProvider registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeMP3 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler) { completionHandler([@"this is a test" dataUsingEncoding:NSUTF8StringEncoding], nil); return nil; }]; [itemProvider registerDataRepresentationForTypeIdentifier:@"org.WebKit.TestWebKitAPI.custom-pasteboard-type" visibility:NSItemProviderRepresentationVisibilityAll loadHandler:^NSProgress *(DataLoadCompletionBlock completionHandler) { completionHandler([@"this is another test" dataUsingEncoding:NSUTF8StringEncoding], nil); return nil; }]; [simulator setExternalItemProviders:@[ itemProvider.get() ]]; [itemProvider setPreferredPresentationStyle:UIPreferredPresentationStyleInline]; [simulator runFrom:CGPointMake(300, 375) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ }, @"drop": @{ } }); } TEST(DragAndDropTests, DataTransferSetDataValidURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"select(rich)"]; [webView stringByEvaluatingJavaScript:@"customData = { 'url' : 'https://webkit.org/b/123' }"]; [webView stringByEvaluatingJavaScript:@"writeCustomData = true"]; __block bool done = false; [simulator setOverridePerformDropBlock:^NSArray *(id session) { EXPECT_EQ(1UL, session.items.count); auto *item = session.items[0].itemProvider; EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(__bridge NSString *)kUTTypeURL]); EXPECT_TRUE([item canLoadObjectOfClass: [NSURL class]]); [item loadObjectOfClass:[NSURL class] completionHandler:^(id url, NSError *error) { EXPECT_TRUE([url isKindOfClass: [NSURL class]]); EXPECT_WK_STREQ([(NSURL *)url absoluteString], @"https://webkit.org/b/123"); done = true; }]; return session.items; }]; [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/uri-list": @"", }, @"drop": @{ @"text/uri-list": @"https://webkit.org/b/123", } }); TestWebKitAPI::Util::run(&done); } TEST(DragAndDropTests, DataTransferSetDataUnescapedURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"select(rich)"]; [webView stringByEvaluatingJavaScript:@"customData = { 'url' : 'http://webkit.org/b/\u4F60\u597D;?x=8 + 6' }"]; [webView stringByEvaluatingJavaScript:@"writeCustomData = true"]; __block bool done = false; [simulator setOverridePerformDropBlock:^NSArray *(id session) { EXPECT_EQ(1UL, session.items.count); auto *item = session.items[0].itemProvider; EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(__bridge NSString *)kUTTypeURL]); EXPECT_TRUE([item canLoadObjectOfClass: [NSURL class]]); [item loadObjectOfClass:[NSURL class] completionHandler:^(id url, NSError *error) { EXPECT_TRUE([url isKindOfClass: [NSURL class]]); EXPECT_WK_STREQ([(NSURL *)url absoluteString], @"http://webkit.org/b/%E4%BD%A0%E5%A5%BD;?x=8%20+%206"); done = true; }]; return session.items; }]; [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/uri-list": @"", }, @"drop": @{ @"text/uri-list": @"http://webkit.org/b/\u4F60\u597D;?x=8 + 6", } }); TestWebKitAPI::Util::run(&done); } TEST(DragAndDropTests, DataTransferSetDataInvalidURL) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"select(rich)"]; [webView stringByEvaluatingJavaScript:@"customData = { 'url' : 'some random string' }"]; [webView stringByEvaluatingJavaScript:@"writeCustomData = true"]; [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; NSArray *registeredTypes = [simulator.get().sourceItemProviders.firstObject registeredTypeIdentifiers]; EXPECT_FALSE([registeredTypes containsObject:(__bridge NSString *)kUTTypeURL]); checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/uri-list": @"", }, @"drop": @{ @"text/uri-list": @"some random string", } }); } TEST(DragAndDropTests, DataTransferSanitizeHTML) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"dump-datatransfer-types"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [webView stringByEvaluatingJavaScript:@"select(rich)"]; [webView stringByEvaluatingJavaScript:@"customData = { 'text/html' : '" "hello, world' }"]; [webView stringByEvaluatingJavaScript:@"writeCustomData = true"]; __block bool done = false; [simulator setOverridePerformDropBlock:^NSArray *(id session) { EXPECT_EQ(1UL, session.items.count); auto *item = session.items[0].itemProvider; EXPECT_TRUE([item.registeredTypeIdentifiers containsObject:(__bridge NSString *)kUTTypeHTML]); [item loadDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeHTML completionHandler:^(NSData *data, NSError *error) { auto markup = adoptNS([[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding]); EXPECT_TRUE([markup containsString:@"hello"]); EXPECT_TRUE([markup containsString:@", world"]); EXPECT_FALSE([markup containsString:@"secret"]); EXPECT_FALSE([markup containsString:@"dangerousCode"]); done = true; }]; return session.items; }]; [simulator runFrom:CGPointMake(50, 225) to:CGPointMake(50, 375)]; checkJSONWithLogging([webView stringByEvaluatingJavaScript:@"output.value"], @{ @"dragover": @{ @"text/html": @"", }, @"drop": @{ @"text/html": @"hello, world", } }); TestWebKitAPI::Util::run(&done); } static BOOL isCompletelyWhite(UIImage *image) { auto data = adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))); auto* dataPtr = CFDataGetBytePtr(data.get()); int imageWidth = image.size.width; for (int row = 0; row < image.size.height; ++row) { for (int column = 0; column < imageWidth; ++column) { int pixelOffset = ((imageWidth * row) + column) * 4; if (dataPtr[pixelOffset] != 0xFF || dataPtr[pixelOffset + 1] != 0xFF || dataPtr[pixelOffset + 2] != 0xFF) return NO; } } return YES; } TEST(DragAndDropTests, DropPreviewForImageInEditableArea) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"image-and-contenteditable"]; // Ensure that the resulting snapshot on drop contains only the dragged image. [webView stringByEvaluatingJavaScript:@"editor.style.border = 'none'; editor.style.outline = 'none'"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 50) to:CGPointMake(100, 300)]; NSArray *dropPreviews = [simulator dropPreviews]; NSArray *delayedDropPreviews = [simulator delayedDropPreviews]; EXPECT_EQ(1U, dropPreviews.count); EXPECT_EQ(1U, delayedDropPreviews.count); EXPECT_EQ(UITargetedDragPreview.class, [dropPreviews.firstObject class]); EXPECT_EQ(UITargetedDragPreview.class, [delayedDropPreviews.firstObject class]); UITargetedDragPreview *finalPreview = (UITargetedDragPreview *)delayedDropPreviews.firstObject; EXPECT_EQ(UIImageView.class, finalPreview.view.class); EXPECT_FALSE(isCompletelyWhite([(UIImageView *)finalPreview.view image])); } TEST(DragAndDropTests, DropUserSelectAllUserDragElementDiv) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"contenteditable-user-select-user-drag"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(100, 100) to:CGPointMake(100, 300)]; NSArray *liftPreviews = [simulator liftPreviews]; EXPECT_EQ(1U, liftPreviews.count); EXPECT_EQ(UITargetedDragPreview.class, [liftPreviews.firstObject class]); checkCGRectIsEqualToCGRectWithLogging({ { 0, 0 }, { 200, 200 } }, [simulator liftPreviews][0].view.frame); EXPECT_WK_STREQ(@"Text", [webView stringByEvaluatingJavaScript:@"document.getElementById(\"editor\").textContent"]); } TEST(DragAndDropTests, SuggestedNameContainsDot) { auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]); [webView synchronouslyLoadTestPageNamed:@"gif-and-file-input"]; auto firstItem = adoptNS([[NSItemProvider alloc] init]); [firstItem registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypePNG withData:testIconImageData()]; [firstItem setSuggestedName:@"one.foo"]; auto secondItem = adoptNS([[NSItemProvider alloc] init]); [secondItem registerDataRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypePNG withData:testIconImageData()]; [secondItem setSuggestedName:@"two.foo.PNG"]; auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator setExternalItemProviders:@[ firstItem.get(), secondItem.get() ]]; [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(100, 300)]; EXPECT_WK_STREQ("one.foo.png (image/png)\ntwo.foo.PNG (image/png)", [webView stringByEvaluatingJavaScript:@"output.innerText"]); } TEST(DragAndDropTests, CanStartDragOnModel) { auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); for (_WKExperimentalFeature *feature in [WKPreferences _experimentalFeatures]) { if ([feature.key isEqualToString:@"ModelElementEnabled"]) [[configuration preferences] _setEnabled:YES forFeature:feature]; } // FIXME: Remove this after is fixed. // It should not be necessary to use WKURLSchemeHandler here, but CFNetwork does not correctly identify USDZ files. auto handler = adoptNS([TestURLSchemeHandler new]); RetainPtr modelData = [NSData dataWithContentsOfURL:[NSBundle.mainBundle URLForResource:@"cube" withExtension:@"usdz" subdirectory:@"TestWebKitAPI.resources"]]; [handler setStartURLSchemeTaskHandler:^(WKWebView *, id task) { NSURLResponse *response = [[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"model/vnd.usdz+zip" expectedContentLength:[modelData length] textEncodingName:nil] autorelease]; [task didReceiveResponse:response]; [task didReceiveData:modelData.get()]; [task didFinish]; }]; auto messageHandler = adoptNS([[ModelLoadingMessageHandler alloc] init]); [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"modelLoading"]; [configuration setURLSchemeHandler:handler.get() forURLScheme:@"model"]; auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()]); [webView synchronouslyLoadHTMLString:@""]; while (![messageHandler didLoadModel]) Util::spinRunLoop(); auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]); [simulator runFrom:CGPointMake(20, 20) to:CGPointMake(100, 100)]; NSArray *registeredTypes = [[simulator sourceItemProviders].firstObject registeredTypeIdentifiers]; EXPECT_WK_STREQ("com.pixar.universal-scene-description-mobile", [registeredTypes firstObject]); } } // namespace TestWebKitAPI #endif // ENABLE(DRAG_SUPPORT) && PLATFORM(IOS_FAMILY) && !PLATFORM(MACCATALYST)