[Attachment Support] Remove WebCore::AttachmentDisplayOptions and friends
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / WKAttachmentTests.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #import "DragAndDropSimulator.h"
29 #import "PlatformUtilities.h"
30 #import "TestNavigationDelegate.h"
31 #import "TestWKWebView.h"
32 #import "WKWebViewConfigurationExtras.h"
33 #import <WebKit/WKPreferencesRefPrivate.h>
34 #import <WebKit/WKWebViewPrivate.h>
35 #import <WebKit/WebArchive.h>
36 #import <WebKit/WebKitPrivate.h>
37 #import <wtf/RetainPtr.h>
38
39 #if PLATFORM(IOS)
40 #import <MobileCoreServices/MobileCoreServices.h>
41 #endif
42
43 #define USES_MODERN_ATTRIBUTED_STRING_CONVERSION ((PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300))
44
45 #if WK_API_ENABLED && !PLATFORM(WATCHOS) && !PLATFORM(TVOS)
46
47 @interface AttachmentUpdateObserver : NSObject <WKUIDelegatePrivate>
48 @property (nonatomic, readonly) NSArray *inserted;
49 @property (nonatomic, readonly) NSArray *removed;
50 @end
51
52 @implementation AttachmentUpdateObserver {
53     RetainPtr<NSMutableArray<_WKAttachment *>> _inserted;
54     RetainPtr<NSMutableArray<_WKAttachment *>> _removed;
55     RetainPtr<NSMutableDictionary<NSString *, NSString *>> _identifierToSourceMap;
56 }
57
58 - (instancetype)init
59 {
60     if (self = [super init]) {
61         _inserted = adoptNS([[NSMutableArray alloc] init]);
62         _removed = adoptNS([[NSMutableArray alloc] init]);
63         _identifierToSourceMap = adoptNS([[NSMutableDictionary alloc] init]);
64     }
65     return self;
66 }
67
68 - (NSArray<_WKAttachment *> *)inserted
69 {
70     return _inserted.get();
71 }
72
73 - (NSArray<_WKAttachment *> *)removed
74 {
75     return _removed.get();
76 }
77
78 - (NSString *)sourceForIdentifier:(NSString *)identifier
79 {
80     return [_identifierToSourceMap objectForKey:identifier];
81 }
82
83 - (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment withSource:(NSString *)source
84 {
85     [_inserted addObject:attachment];
86     if (source)
87         [_identifierToSourceMap setObject:source forKey:attachment.uniqueIdentifier];
88 }
89
90 - (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment
91 {
92     [_removed addObject:attachment];
93 }
94
95 @end
96
97 namespace TestWebKitAPI {
98
99 class ObserveAttachmentUpdatesForScope {
100 public:
101     ObserveAttachmentUpdatesForScope(TestWKWebView *webView)
102         : m_webView(webView)
103     {
104         m_previousDelegate = webView.UIDelegate;
105         m_observer = adoptNS([[AttachmentUpdateObserver alloc] init]);
106         webView.UIDelegate = m_observer.get();
107     }
108
109     ~ObserveAttachmentUpdatesForScope()
110     {
111         [m_webView setUIDelegate:m_previousDelegate.get()];
112     }
113
114     AttachmentUpdateObserver *observer() const { return m_observer.get(); }
115
116     void expectAttachmentUpdates(NSArray<_WKAttachment *> *removed, NSArray<_WKAttachment *> *inserted)
117     {
118         BOOL removedAttachmentsMatch = [[NSSet setWithArray:observer().removed] isEqual:[NSSet setWithArray:removed]];
119         if (!removedAttachmentsMatch)
120             NSLog(@"Expected removed attachments: %@ to match %@.", observer().removed, removed);
121         EXPECT_TRUE(removedAttachmentsMatch);
122
123         BOOL insertedAttachmentsMatch = [[NSSet setWithArray:observer().inserted] isEqual:[NSSet setWithArray:inserted]];
124         if (!insertedAttachmentsMatch)
125             NSLog(@"Expected inserted attachments: %@ to match %@.", observer().inserted, inserted);
126         EXPECT_TRUE(insertedAttachmentsMatch);
127     }
128
129     void expectSourceForIdentifier(NSString *expectedSource, NSString *identifier)
130     {
131         NSString *observedSource = [observer() sourceForIdentifier:identifier];
132         BOOL success = observedSource == expectedSource || [observedSource isEqualToString:expectedSource];
133         EXPECT_TRUE(success);
134         if (!success)
135             NSLog(@"Expected source: %@ but observed: %@", expectedSource, observedSource);
136     }
137
138 private:
139     RetainPtr<AttachmentUpdateObserver> m_observer;
140     RetainPtr<TestWKWebView> m_webView;
141     RetainPtr<id> m_previousDelegate;
142 };
143
144 }
145
146 @interface TestWKWebView (AttachmentTesting)
147 @end
148
149 static NSString *attachmentEditingTestMarkup = @"<meta name='viewport' content='width=device-width, initial-scale=1'>"
150     "<script>focus = () => document.body.focus()</script>"
151     "<body onload=focus() contenteditable></body>";
152
153 static RetainPtr<TestWKWebView> webViewForTestingAttachments(CGSize webViewSize, WKWebViewConfiguration *configuration)
154 {
155     configuration._attachmentElementEnabled = YES;
156     WKPreferencesSetCustomPasteboardDataEnabled((WKPreferencesRef)[configuration preferences], YES);
157
158     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, webViewSize.width, webViewSize.height) configuration:configuration]);
159     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
160
161     return webView;
162 }
163
164 static RetainPtr<TestWKWebView> webViewForTestingAttachments(CGSize webViewSize)
165 {
166     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
167     return webViewForTestingAttachments(webViewSize, configuration.get());
168 }
169
170 static RetainPtr<TestWKWebView> webViewForTestingAttachments()
171 {
172     return webViewForTestingAttachments(CGSizeMake(500, 500));
173 }
174
175 static NSData *testZIPData()
176 {
177     NSURL *zipFileURL = [[NSBundle mainBundle] URLForResource:@"compressed-files" withExtension:@"zip" subdirectory:@"TestWebKitAPI.resources"];
178     return [NSData dataWithContentsOfURL:zipFileURL];
179 }
180
181 static NSData *testHTMLData()
182 {
183     return [@"<a href='#'>This is some HTML data</a>" dataUsingEncoding:NSUTF8StringEncoding];
184 }
185
186 static NSURL *testImageFileURL()
187 {
188     return [[NSBundle mainBundle] URLForResource:@"icon" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"];
189 }
190
191 static NSData *testImageData()
192 {
193     return [NSData dataWithContentsOfURL:testImageFileURL()];
194 }
195
196 static NSURL *testGIFFileURL()
197 {
198     return [[NSBundle mainBundle] URLForResource:@"apple" withExtension:@"gif" subdirectory:@"TestWebKitAPI.resources"];
199 }
200
201 static NSData *testGIFData()
202 {
203     return [NSData dataWithContentsOfURL:testGIFFileURL()];
204 }
205
206 static NSURL *testPDFFileURL()
207 {
208     return [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"pdf" subdirectory:@"TestWebKitAPI.resources"];
209 }
210
211 static NSData *testPDFData()
212 {
213     return [NSData dataWithContentsOfURL:testPDFFileURL()];
214 }
215
216 @implementation TestWKWebView (AttachmentTesting)
217
218 - (NSArray<NSString *> *)tagsInBody
219 {
220     return [self objectByEvaluatingJavaScript:@"Array.from(document.body.getElementsByTagName('*')).map(e => e.tagName)"];
221 }
222
223 - (void)expectElementTagsInOrder:(NSArray<NSString *> *)tagNames
224 {
225     auto remainingTags = adoptNS([tagNames mutableCopy]);
226     NSArray<NSString *> *tagsInBody = self.tagsInBody;
227     for (NSString *tag in tagsInBody.reverseObjectEnumerator) {
228         if ([tag isEqualToString:[remainingTags lastObject]])
229             [remainingTags removeLastObject];
230         if (![remainingTags count])
231             break;
232     }
233     EXPECT_EQ([remainingTags count], 0U);
234     if ([remainingTags count])
235         NSLog(@"Expected to find ordered tags: %@ in: %@", tagNames, tagsInBody);
236 }
237
238 - (void)expectElementCount:(NSInteger)count tagName:(NSString *)tagName
239 {
240     NSString *script = [NSString stringWithFormat:@"document.querySelectorAll('%@').length", tagName];
241     EXPECT_EQ(count, [self stringByEvaluatingJavaScript:script].integerValue);
242 }
243
244 - (void)expectElementTag:(NSString *)tagName toComeBefore:(NSString *)otherTagName
245 {
246     [self expectElementTagsInOrder:@[tagName, otherTagName]];
247 }
248
249 - (BOOL)_synchronouslyExecuteEditCommand:(NSString *)command argument:(NSString *)argument
250 {
251     __block bool done = false;
252     __block bool success;
253     [self _executeEditCommand:command argument:argument completion:^(BOOL completionSuccess) {
254         done = true;
255         success = completionSuccess;
256     }];
257     TestWebKitAPI::Util::run(&done);
258     return success;
259 }
260
261 - (_WKAttachment *)synchronouslyInsertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType
262 {
263     __block bool done = false;
264     RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFileWrapper:fileWrapper contentType:contentType completion:^(BOOL) {
265         done = true;
266     }];
267     TestWebKitAPI::Util::run(&done);
268     return attachment.autorelease();
269 }
270
271 - (_WKAttachment *)synchronouslyInsertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data
272 {
273     __block bool done = false;
274     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
275     if (filename)
276         [fileWrapper setPreferredFilename:filename];
277     RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFileWrapper:fileWrapper.get() contentType:contentType completion:^(BOOL) {
278         done = true;
279     }];
280     TestWebKitAPI::Util::run(&done);
281     return attachment.autorelease();
282 }
283
284 - (CGPoint)attachmentElementMidPoint
285 {
286     __block CGPoint midPoint;
287     __block bool doneEvaluatingScript = false;
288     [self evaluateJavaScript:@"r = document.querySelector('attachment').getBoundingClientRect(); [r.left + r.width / 2, r.top + r.height / 2]" completionHandler:^(NSArray<NSNumber *> *result, NSError *) {
289         midPoint = CGPointMake(result.firstObject.floatValue, result.lastObject.floatValue);
290         doneEvaluatingScript = true;
291     }];
292     TestWebKitAPI::Util::run(&doneEvaluatingScript);
293     return midPoint;
294 }
295
296 - (CGSize)attachmentElementSize
297 {
298     __block CGSize size;
299     __block bool doneEvaluatingScript = false;
300     [self evaluateJavaScript:@"r = document.querySelector('attachment').getBoundingClientRect(); [r.width, r.height]" completionHandler:^(NSArray<NSNumber *> *sizeResult, NSError *) {
301         size = CGSizeMake(sizeResult.firstObject.floatValue, sizeResult.lastObject.floatValue);
302         doneEvaluatingScript = true;
303     }];
304     TestWebKitAPI::Util::run(&doneEvaluatingScript);
305     return size;
306 }
307
308 - (void)waitForAttachmentElementSizeToBecome:(CGSize)expectedSize
309 {
310     while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]) {
311         if (CGSizeEqualToSize(self.attachmentElementSize, expectedSize))
312             break;
313     }
314 }
315
316 - (BOOL)hasAttribute:(NSString *)attributeName forQuerySelector:(NSString *)querySelector
317 {
318     return [self stringByEvaluatingJavaScript:[NSString stringWithFormat:@"document.querySelector('%@').hasAttribute('%@')", querySelector, attributeName]].boolValue;
319 }
320
321 - (NSString *)valueOfAttribute:(NSString *)attributeName forQuerySelector:(NSString *)querySelector
322 {
323     return [self stringByEvaluatingJavaScript:[NSString stringWithFormat:@"document.querySelector('%@').getAttribute('%@')", querySelector, attributeName]];
324 }
325
326 - (void)expectUpdatesAfterCommand:(NSString *)command withArgument:(NSString *)argument expectedRemovals:(NSArray<_WKAttachment *> *)removed expectedInsertions:(NSArray<_WKAttachment *> *)inserted
327 {
328     TestWebKitAPI::ObserveAttachmentUpdatesForScope observer(self);
329     EXPECT_TRUE([self _synchronouslyExecuteEditCommand:command argument:argument]);
330     observer.expectAttachmentUpdates(removed, inserted);
331 }
332
333 @end
334
335 @implementation NSData (AttachmentTesting)
336
337 - (NSString *)shortDescription
338 {
339     return [NSString stringWithFormat:@"<%tu bytes>", self.length];
340 }
341
342 @end
343
344 @implementation _WKAttachment (AttachmentTesting)
345
346 - (void)synchronouslySetFileWrapper:(NSFileWrapper *)fileWrapper newContentType:(NSString *)newContentType error:(NSError **)error
347 {
348     __block RetainPtr<NSError> resultError;
349     __block bool done = false;
350
351     [self setFileWrapper:fileWrapper contentType:newContentType completion:^(NSError *error) {
352         resultError = error;
353         done = true;
354     }];
355
356     TestWebKitAPI::Util::run(&done);
357
358     if (error)
359         *error = resultError.autorelease();
360 }
361
362 - (void)synchronouslySetData:(NSData *)data newContentType:(NSString *)newContentType newFilename:(NSString *)newFilename error:(NSError **)error
363 {
364     __block RetainPtr<NSError> resultError;
365     __block bool done = false;
366     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
367     if (newFilename)
368         [fileWrapper setPreferredFilename:newFilename];
369
370     [self setFileWrapper:fileWrapper.get() contentType:newContentType completion:^(NSError *error) {
371         resultError = error;
372         done = true;
373     }];
374
375     TestWebKitAPI::Util::run(&done);
376
377     if (error)
378         *error = resultError.autorelease();
379 }
380
381 - (void)expectRequestedDataToBe:(NSData *)expectedData
382 {
383     NSError *requestError = nil;
384     _WKAttachmentInfo *info = self.info;
385
386     BOOL observedDataIsEqualToExpectedData = info.data == expectedData || [info.data isEqualToData:expectedData];
387     EXPECT_TRUE(observedDataIsEqualToExpectedData);
388     if (!observedDataIsEqualToExpectedData) {
389         NSLog(@"Expected data: %@ but observed: %@ for %@", [expectedData shortDescription], [info.data shortDescription], self);
390         NSLog(@"Observed error: %@ while reading data for %@", requestError, self);
391     }
392 }
393
394 @end
395
396 static void runTestWithTemporaryFolder(void(^runTest)(NSURL *folderURL))
397 {
398     NSFileManager *defaultManager = [NSFileManager defaultManager];
399     auto temporaryFolder = retainPtr([NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"folder-%@", NSUUID.UUID]] isDirectory:YES]);
400     [defaultManager removeItemAtURL:temporaryFolder.get() error:nil];
401     [defaultManager createDirectoryAtURL:temporaryFolder.get() withIntermediateDirectories:NO attributes:nil error:nil];
402     [testImageData() writeToURL:[temporaryFolder.get() URLByAppendingPathComponent:@"image.png" isDirectory:NO] atomically:YES];
403     [testZIPData() writeToURL:[temporaryFolder.get() URLByAppendingPathComponent:@"archive.zip" isDirectory:NO] atomically:YES];
404     @try {
405         runTest(temporaryFolder.get());
406     } @finally {
407         [[NSFileManager defaultManager] removeItemAtURL:temporaryFolder.get() error:nil];
408     }
409 }
410
411 static void simulateFolderDragWithURL(DragAndDropSimulator *simulator, NSURL *folderURL)
412 {
413 #if PLATFORM(MAC)
414     [simulator writePromisedFiles:@[ folderURL ]];
415 #else
416     auto folderProvider = adoptNS([[NSItemProvider alloc] init]);
417     [folderProvider setSuggestedName:folderURL.lastPathComponent];
418     [folderProvider setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
419     [folderProvider registerFileRepresentationForTypeIdentifier:(__bridge NSString *)kUTTypeFolder fileOptions:0 visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[&] (void(^completion)(NSURL *, BOOL, NSError *)) -> NSProgress * {
420         completion(folderURL, NO, nil);
421         return nil;
422     }];
423     simulator.externalItemProviders = @[ folderProvider.get() ];
424 #endif
425 }
426
427 #pragma mark - Platform testing helper functions
428
429 #if PLATFORM(MAC)
430
431 BOOL isCompletelyTransparent(NSImage *image)
432 {
433     auto representation = adoptNS([[NSBitmapImageRep alloc] initWithData:image.TIFFRepresentation]);
434     for (int row = 0; row < image.size.height; ++row) {
435         for (int column = 0; column < image.size.width; ++column) {
436             if ([representation colorAtX:column y:row].alphaComponent)
437                 return false;
438         }
439     }
440     return true;
441 }
442
443 #endif
444
445 #if PLATFORM(IOS)
446
447 typedef void(^ItemProviderDataLoadHandler)(NSData *, NSError *);
448
449 @implementation NSItemProvider (AttachmentTesting)
450
451 - (void)registerData:(NSData *)data type:(NSString *)type
452 {
453     [self registerDataRepresentationForTypeIdentifier:type visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedData = retainPtr(data)] (ItemProviderDataLoadHandler completionHandler) -> NSProgress * {
454         completionHandler(protectedData.get(), nil);
455         return nil;
456     }];
457 }
458
459 - (void)expectType:(NSString *)type withData:(NSData *)expectedData
460 {
461     BOOL containsType = [self.registeredTypeIdentifiers containsObject:type];
462     EXPECT_TRUE(containsType);
463     if (!containsType) {
464         NSLog(@"Expected: %@ to contain %@", self, type);
465         return;
466     }
467
468     __block bool done = false;
469     [self loadDataRepresentationForTypeIdentifier:type completionHandler:^(NSData *observedData, NSError *error) {
470         EXPECT_TRUE([observedData isEqualToData:expectedData]);
471         if (![observedData isEqualToData:expectedData])
472             NSLog(@"Expected data: <%tu bytes> to be equal to data: <%tu bytes>", observedData.length, expectedData.length);
473         EXPECT_TRUE(!error);
474         if (error)
475             NSLog(@"Encountered error when loading data: %@", error);
476         done = true;
477     }];
478     TestWebKitAPI::Util::run(&done);
479 }
480
481 @end
482
483 #endif // PLATFORM(IOS)
484
485 void platformCopyRichTextWithMultipleAttachments()
486 {
487     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(__bridge NSString *)kUTTypePNG]);
488     auto pdf = adoptNS([[NSTextAttachment alloc] initWithData:testPDFData() ofType:(__bridge NSString *)kUTTypePDF]);
489     auto zip = adoptNS([[NSTextAttachment alloc] initWithData:testZIPData() ofType:(__bridge NSString *)kUTTypeZipArchive]);
490
491     auto richText = adoptNS([[NSMutableAttributedString alloc] init]);
492     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:image.get()]];
493     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:pdf.get()]];
494     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:zip.get()]];
495
496 #if PLATFORM(MAC)
497     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
498     [pasteboard clearContents];
499     [pasteboard writeObjects:@[ richText.get() ]];
500 #elif PLATFORM(IOS)
501     auto item = adoptNS([[NSItemProvider alloc] init]);
502     [item registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
503     [UIPasteboard generalPasteboard].itemProviders = @[ item.get() ];
504 #endif
505 }
506
507 void platformCopyRichTextWithImage()
508 {
509     auto richText = adoptNS([[NSMutableAttributedString alloc] init]);
510     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(__bridge NSString *)kUTTypePNG]);
511
512     [richText appendAttributedString:[[[NSAttributedString alloc] initWithString:@"Lorem ipsum "] autorelease]];
513     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:image.get()]];
514     [richText appendAttributedString:[[[NSAttributedString alloc] initWithString:@" dolor sit amet."] autorelease]];
515
516 #if PLATFORM(MAC)
517     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
518     [pasteboard clearContents];
519     [pasteboard writeObjects:@[ richText.get() ]];
520 #elif PLATFORM(IOS)
521     auto item = adoptNS([[NSItemProvider alloc] init]);
522     [item registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
523     [UIPasteboard generalPasteboard].itemProviders = @[ item.get() ];
524 #endif
525 }
526
527 void platformCopyPNG()
528 {
529 #if PLATFORM(MAC)
530     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
531     [pasteboard declareTypes:@[NSPasteboardTypePNG] owner:nil];
532     [pasteboard setData:testImageData() forType:NSPasteboardTypePNG];
533 #elif PLATFORM(IOS)
534     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
535     auto item = adoptNS([[UIItemProvider alloc] init]);
536     [item setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
537     [item registerData:testImageData() type:(__bridge NSString *)kUTTypePNG];
538     pasteboard.itemProviders = @[ item.get() ];
539 #endif
540 }
541
542 #if PLATFORM(MAC)
543 typedef NSImage PlatformImage;
544 #else
545 typedef UIImage PlatformImage;
546 #endif
547
548 PlatformImage *platformImageWithData(NSData *data)
549 {
550 #if PLATFORM(MAC)
551     return [[[NSImage alloc] initWithData:data] autorelease];
552 #else
553     return [UIImage imageWithData:data];
554 #endif
555 }
556
557 namespace TestWebKitAPI {
558
559 #pragma mark - Platform-agnostic tests
560
561 TEST(WKAttachmentTests, AttachmentElementInsertion)
562 {
563     auto webView = webViewForTestingAttachments();
564     RetainPtr<_WKAttachment> firstAttachment;
565     RetainPtr<_WKAttachment> secondAttachment;
566     {
567         ObserveAttachmentUpdatesForScope observer(webView.get());
568         // Use the given content type for the attachment element's type.
569         firstAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo" contentType:@"text/html" data:testHTMLData()];
570         EXPECT_WK_STREQ(@"foo", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
571         EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
572         EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
573         observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
574     }
575
576     _WKAttachmentInfo *info = [firstAttachment info];
577     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
578     EXPECT_TRUE([info.contentType isEqualToString:@"text/html"]);
579     EXPECT_TRUE([info.name isEqualToString:@"foo"]);
580     EXPECT_EQ(info.filePath.length, 0U);
581
582     {
583         ObserveAttachmentUpdatesForScope scope(webView.get());
584         // Since no content type is explicitly specified, compute it from the file extension.
585         [webView _executeEditCommand:@"DeleteBackward" argument:nil completion:nil];
586         secondAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"bar.png" contentType:nil data:testImageData()];
587         EXPECT_WK_STREQ(@"bar.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
588         EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
589         EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
590         scope.expectAttachmentUpdates(@[ firstAttachment.get() ], @[ secondAttachment.get() ]);
591     }
592
593     [firstAttachment expectRequestedDataToBe:testHTMLData()];
594     [secondAttachment expectRequestedDataToBe:testImageData()];
595     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
596     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
597     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
598 }
599
600 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingAndDeletingNewline)
601 {
602     auto webView = webViewForTestingAttachments();
603     RetainPtr<_WKAttachment> attachment;
604     {
605         ObserveAttachmentUpdatesForScope observer(webView.get());
606         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
607         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
608     }
609     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
610     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
611     [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body)"];
612     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
613
614     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
615
616     _WKAttachmentInfo *info = [attachment info];
617     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
618     EXPECT_TRUE([info.contentType isEqualToString:@"text/plain"]);
619     EXPECT_TRUE([info.name isEqualToString:@"foo.txt"]);
620     EXPECT_EQ(info.filePath.length, 0U);
621
622     [webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
623     [attachment expectRequestedDataToBe:testHTMLData()];
624 }
625
626 TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing)
627 {
628     auto webView = webViewForTestingAttachments();
629     RetainPtr<NSData> htmlData = testHTMLData();
630     RetainPtr<_WKAttachment> attachment;
631     {
632         ObserveAttachmentUpdatesForScope observer(webView.get());
633         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
634         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
635     }
636     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
637     [attachment expectRequestedDataToBe:htmlData.get()];
638
639     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
640     [attachment expectRequestedDataToBe:htmlData.get()];
641
642     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
643     [attachment expectRequestedDataToBe:htmlData.get()];
644
645     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
646     [attachment expectRequestedDataToBe:htmlData.get()];
647
648     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
649     [attachment expectRequestedDataToBe:htmlData.get()];
650 }
651
652 TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles)
653 {
654     auto webView = webViewForTestingAttachments();
655     RetainPtr<_WKAttachment> attachment;
656     [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello"];
657     {
658         ObserveAttachmentUpdatesForScope observer(webView.get());
659         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
660         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
661     }
662     [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"World" expectedRemovals:@[] expectedInsertions:@[]];
663     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
664     [webView expectUpdatesAfterCommand:@"ToggleBold" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
665     [webView expectUpdatesAfterCommand:@"ToggleItalic" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
666     [webView expectUpdatesAfterCommand:@"ToggleUnderline" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
667     [attachment expectRequestedDataToBe:testHTMLData()];
668     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
669     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
670     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
671
672     // Inserting text should delete the current selection, removing the attachment in the process.
673     [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"foo" expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
674     [attachment expectRequestedDataToBe:testHTMLData()];
675 }
676
677 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingLists)
678 {
679     auto webView = webViewForTestingAttachments();
680     RetainPtr<_WKAttachment> attachment;
681     {
682         ObserveAttachmentUpdatesForScope observer(webView.get());
683         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
684         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
685     }
686     [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
687     // This edit command behaves more like a "toggle", and will actually break us out of the list we just inserted.
688     [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
689     [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
690     [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
691     [attachment expectRequestedDataToBe:testHTMLData()];
692     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
693     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
694     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
695 }
696
697 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingRichMarkup)
698 {
699     auto webView = webViewForTestingAttachments();
700     RetainPtr<_WKAttachment> attachment;
701     {
702         ObserveAttachmentUpdatesForScope observer(webView.get());
703         [webView _synchronouslyExecuteEditCommand:@"InsertHTML" argument:@"<div><strong><attachment src='cid:123-4567' title='a'></attachment></strong></div>"];
704         attachment = observer.observer().inserted[0];
705         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
706         observer.expectSourceForIdentifier(@"cid:123-4567", [attachment uniqueIdentifier]);
707     }
708     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
709     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
710     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
711     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
712     {
713         ObserveAttachmentUpdatesForScope observer(webView.get());
714         [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').remove()"];
715         [webView waitForNextPresentationUpdate];
716         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
717     }
718     [attachment expectRequestedDataToBe:nil];
719 }
720
721 TEST(WKAttachmentTests, AttachmentUpdatesWhenCuttingAndPasting)
722 {
723     auto webView = webViewForTestingAttachments();
724     RetainPtr<_WKAttachment> attachment;
725     {
726         ObserveAttachmentUpdatesForScope observer(webView.get());
727         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
728         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
729     }
730     [attachment expectRequestedDataToBe:testHTMLData()];
731     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
732     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
733     {
734         ObserveAttachmentUpdatesForScope observer(webView.get());
735         [webView _synchronouslyExecuteEditCommand:@"Cut" argument:nil];
736         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
737     }
738     [attachment expectRequestedDataToBe:testHTMLData()];
739     {
740         ObserveAttachmentUpdatesForScope observer(webView.get());
741         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
742         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
743     }
744     [attachment expectRequestedDataToBe:testHTMLData()];
745     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
746     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
747     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
748 }
749
750 TEST(WKAttachmentTests, AttachmentDataForEmptyFile)
751 {
752     auto webView = webViewForTestingAttachments();
753     RetainPtr<_WKAttachment> attachment;
754     {
755         ObserveAttachmentUpdatesForScope observer(webView.get());
756         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"empty.txt" contentType:@"text/plain" data:[NSData data]];
757         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
758     }
759     [attachment expectRequestedDataToBe:[NSData data]];
760     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
761     {
762         ObserveAttachmentUpdatesForScope scope(webView.get());
763         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
764         scope.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
765     }
766     [attachment expectRequestedDataToBe:[NSData data]];
767 }
768
769 TEST(WKAttachmentTests, DropFolderAsAttachmentAndMoveByDragging)
770 {
771     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
772     [configuration _setAttachmentElementEnabled:YES];
773
774     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
775     [[simulator webView] synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
776
777     runTestWithTemporaryFolder([simulator] (NSURL *folderURL) {
778         simulateFolderDragWithURL(simulator.get(), folderURL);
779         [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(50, 50)];
780
781         TestWKWebView *webView = [simulator webView];
782         auto attachment = retainPtr([simulator insertedAttachments].firstObject);
783         EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
784         EXPECT_WK_STREQ((__bridge NSString *)kUTTypeDirectory, [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
785         EXPECT_WK_STREQ(folderURL.lastPathComponent, [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
786
787         NSFileWrapper *image = [attachment info].fileWrapper.fileWrappers[@"image.png"];
788         NSFileWrapper *archive = [attachment info].fileWrapper.fileWrappers[@"archive.zip"];
789         EXPECT_TRUE([image.regularFileContents isEqualToData:testImageData()]);
790         EXPECT_TRUE([archive.regularFileContents isEqualToData:testZIPData()]);
791
792         [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
793         [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
794         [webView _executeEditCommand:@"InsertHTML" argument:@"<em>foo</em>" completion:nil];
795         [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
796
797         [webView expectElementTag:@"ATTACHMENT" toComeBefore:@"EM"];
798         [simulator clearExternalDragInformation];
799         [simulator runFrom:webView.attachmentElementMidPoint to:CGPointMake(300, 300)];
800         [webView expectElementTag:@"EM" toComeBefore:@"ATTACHMENT"];
801     });
802 }
803
804 TEST(WKAttachmentTests, InsertFolderAndFileWithUnknownExtension)
805 {
806     auto webView = webViewForTestingAttachments();
807     auto file = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testHTMLData()]);
808     [file setPreferredFilename:@"test.foobar"];
809     auto image = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testImageData()]);
810     auto document = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testPDFData()]);
811     auto folder = adoptNS([[NSFileWrapper alloc] initDirectoryWithFileWrappers:@{ @"image.png": image.get(), @"document.pdf": document.get() }]);
812     [folder setPreferredFilename:@"folder"];
813
814     RetainPtr<_WKAttachment> firstAttachment;
815     RetainPtr<_WKAttachment> secondAttachment;
816     {
817         ObserveAttachmentUpdatesForScope observer(webView.get());
818         firstAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:file.get() contentType:nil];
819         observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
820     }
821     {
822         ObserveAttachmentUpdatesForScope observer(webView.get());
823         secondAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:folder.get() contentType:nil];
824         observer.expectAttachmentUpdates(@[ ], @[ secondAttachment.get() ]);
825     }
826
827     auto checkAttachmentConsistency = [webView, file, folder] (_WKAttachment *expectedFileAttachment, _WKAttachment *expectedFolderAttachment) {
828         [webView expectElementCount:2 tagName:@"ATTACHMENT"];
829         EXPECT_TRUE(UTTypeConformsTo((__bridge CFStringRef)[webView valueOfAttribute:@"type" forQuerySelector:@"attachment[title=folder]"], kUTTypeDirectory));
830         EXPECT_TRUE(UTTypeConformsTo((__bridge CFStringRef)[webView valueOfAttribute:@"type" forQuerySelector:@"attachment[title^=test]"], kUTTypeData));
831         EXPECT_WK_STREQ(expectedFileAttachment.uniqueIdentifier, [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment[title^=test]').uniqueIdentifier"]);
832         EXPECT_WK_STREQ(expectedFolderAttachment.uniqueIdentifier, [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment[title=folder]').uniqueIdentifier"]);
833         EXPECT_TRUE([expectedFileAttachment.info.fileWrapper isEqual:file.get()]);
834         EXPECT_TRUE([expectedFolderAttachment.info.fileWrapper isEqual:folder.get()]);
835     };
836
837     checkAttachmentConsistency(firstAttachment.get(), secondAttachment.get());
838
839     {
840         // Swap the two attachments' file wrappers without creating or destroying attachment elements.
841         ObserveAttachmentUpdatesForScope observer(webView.get());
842         [firstAttachment synchronouslySetFileWrapper:folder.get() newContentType:nil error:nil];
843         [secondAttachment synchronouslySetFileWrapper:file.get() newContentType:nil error:nil];
844         observer.expectAttachmentUpdates(@[ ], @[ ]);
845     }
846
847     checkAttachmentConsistency(secondAttachment.get(), firstAttachment.get());
848 }
849
850 TEST(WKAttachmentTests, ChangeAttachmentDataAndFileInformation)
851 {
852     auto webView = webViewForTestingAttachments();
853     RetainPtr<_WKAttachment> attachment;
854     {
855         RetainPtr<NSData> pdfData = testPDFData();
856         ObserveAttachmentUpdatesForScope observer(webView.get());
857         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"test.pdf" contentType:@"application/pdf" data:pdfData.get()];
858         [attachment expectRequestedDataToBe:pdfData.get()];
859         EXPECT_WK_STREQ(@"test.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
860         EXPECT_WK_STREQ(@"application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
861         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
862     }
863     {
864         RetainPtr<NSData> imageData = testImageData();
865         ObserveAttachmentUpdatesForScope observer(webView.get());
866         [attachment synchronouslySetData:imageData.get() newContentType:@"image/png" newFilename:@"icon.png" error:nil];
867         [attachment expectRequestedDataToBe:imageData.get()];
868         EXPECT_WK_STREQ(@"icon.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
869         EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
870         observer.expectAttachmentUpdates(@[ ], @[ ]);
871     }
872     {
873         RetainPtr<NSData> textData = [@"Hello world" dataUsingEncoding:NSUTF8StringEncoding];
874         ObserveAttachmentUpdatesForScope observer(webView.get());
875         // The new content type should be inferred from the file name.
876         [attachment synchronouslySetData:textData.get() newContentType:nil newFilename:@"foo.txt" error:nil];
877         [attachment expectRequestedDataToBe:textData.get()];
878         EXPECT_WK_STREQ(@"foo.txt", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
879         EXPECT_WK_STREQ(@"text/plain", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
880         observer.expectAttachmentUpdates(@[ ], @[ ]);
881     }
882     {
883         RetainPtr<NSData> htmlData = testHTMLData();
884         ObserveAttachmentUpdatesForScope observer(webView.get());
885         [attachment synchronouslySetData:htmlData.get() newContentType:@"text/html" newFilename:@"bar" error:nil];
886         [attachment expectRequestedDataToBe:htmlData.get()];
887         EXPECT_WK_STREQ(@"bar", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
888         EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
889         observer.expectAttachmentUpdates(@[ ], @[ ]);
890     }
891     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[ ]];
892 }
893
894 TEST(WKAttachmentTests, RemoveNewlinesBeforePastedImage)
895 {
896     platformCopyPNG();
897
898     RetainPtr<_WKAttachment> attachment;
899     auto webView = webViewForTestingAttachments();
900     {
901         ObserveAttachmentUpdatesForScope observer(webView.get());
902         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
903         EXPECT_EQ(1U, observer.observer().inserted.count);
904         attachment = observer.observer().inserted[0];
905     }
906
907     auto size = platformImageWithData([attachment info].data).size;
908     EXPECT_EQ(215., size.width);
909     EXPECT_EQ(174., size.height);
910     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').webkitAttachmentIdentifier"]);
911
912     [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body, 0)"];
913     {
914         ObserveAttachmentUpdatesForScope observer(webView.get());
915         [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
916         [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
917         observer.expectAttachmentUpdates(@[ ], @[ ]);
918         [webView expectElementTagsInOrder:@[ @"BR", @"BR", @"IMG" ]];
919     }
920     {
921         ObserveAttachmentUpdatesForScope observer(webView.get());
922         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
923         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
924         observer.expectAttachmentUpdates(@[ ], @[ ]);
925         [webView expectElementCount:0 tagName:@"BR"];
926     }
927 }
928
929 TEST(WKAttachmentTests, CutAndPastePastedImage)
930 {
931     platformCopyPNG();
932
933     RetainPtr<_WKAttachment> attachment;
934     auto webView = webViewForTestingAttachments();
935     {
936         ObserveAttachmentUpdatesForScope observer(webView.get());
937         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
938         EXPECT_EQ(1U, observer.observer().inserted.count);
939         attachment = observer.observer().inserted[0];
940     }
941     {
942         ObserveAttachmentUpdatesForScope observer(webView.get());
943         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
944         [webView _synchronouslyExecuteEditCommand:@"Cut" argument:nil];
945         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
946     }
947     {
948         ObserveAttachmentUpdatesForScope observer(webView.get());
949         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
950         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
951     }
952 }
953
954 TEST(WKAttachmentTests, MovePastedImageByDragging)
955 {
956     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
957     [configuration _setAttachmentElementEnabled:YES];
958     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
959     TestWKWebView *webView = [simulator webView];
960     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
961
962     platformCopyPNG();
963     [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
964     [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
965     [webView _executeEditCommand:@"InsertHTML" argument:@"<strong>text</strong>" completion:nil];
966     [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
967     [webView expectElementTag:@"IMG" toComeBefore:@"STRONG"];
968     [webView expectElementCount:1 tagName:@"IMG"];
969
970     // Drag the attachment element to somewhere below the strong text.
971     [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 350)];
972
973     [webView expectElementTag:@"STRONG" toComeBefore:@"IMG"];
974     [webView expectElementCount:1 tagName:@"IMG"];
975     EXPECT_EQ([simulator insertedAttachments].count, [simulator removedAttachments].count);
976
977     [simulator endDataTransfer];
978 }
979
980 TEST(WKAttachmentTests, InsertPastedAttributedStringContainingImage)
981 {
982     platformCopyRichTextWithImage();
983
984     RetainPtr<_WKAttachment> attachment;
985     auto webView = webViewForTestingAttachments();
986     {
987         ObserveAttachmentUpdatesForScope observer(webView.get());
988         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
989         EXPECT_EQ(0U, observer.observer().removed.count);
990         EXPECT_EQ(1U, observer.observer().inserted.count);
991         attachment = observer.observer().inserted[0];
992     }
993
994     [attachment expectRequestedDataToBe:testImageData()];
995     EXPECT_WK_STREQ("Lorem ipsum  dolor sit amet.", [webView stringByEvaluatingJavaScript:@"document.body.textContent"]);
996     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').webkitAttachmentIdentifier"]);
997
998     {
999         ObserveAttachmentUpdatesForScope observer(webView.get());
1000         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1001         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1002         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
1003     }
1004 }
1005
1006 TEST(WKAttachmentTests, InsertPastedAttributedStringContainingMultipleAttachments)
1007 {
1008     platformCopyRichTextWithMultipleAttachments();
1009
1010     RetainPtr<_WKAttachment> imageAttachment;
1011     RetainPtr<_WKAttachment> zipAttachment;
1012     RetainPtr<_WKAttachment> pdfAttachment;
1013     auto webView = webViewForTestingAttachments();
1014     {
1015         ObserveAttachmentUpdatesForScope observer(webView.get());
1016         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1017         EXPECT_EQ(0U, observer.observer().removed.count);
1018         EXPECT_EQ(3U, observer.observer().inserted.count);
1019         for (_WKAttachment *attachment in observer.observer().inserted) {
1020             NSData *data = attachment.info.data;
1021             if ([data isEqualToData:testZIPData()])
1022                 zipAttachment = attachment;
1023             else if ([data isEqualToData:testPDFData()])
1024                 pdfAttachment = attachment;
1025             else if ([data isEqualToData:testImageData()])
1026                 imageAttachment = attachment;
1027         }
1028     }
1029
1030     EXPECT_TRUE(zipAttachment && imageAttachment && pdfAttachment);
1031     [webView expectElementCount:2 tagName:@"ATTACHMENT"];
1032     [webView expectElementCount:1 tagName:@"IMG"];
1033     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1034
1035     NSString *zipAttachmentType = [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"];
1036 #if USES_MODERN_ATTRIBUTED_STRING_CONVERSION
1037     EXPECT_WK_STREQ("application/zip", zipAttachmentType);
1038 #else
1039     EXPECT_WK_STREQ("application/octet-stream", zipAttachmentType);
1040 #endif
1041
1042     EXPECT_WK_STREQ([imageAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').webkitAttachmentIdentifier"]);
1043     EXPECT_WK_STREQ([pdfAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].uniqueIdentifier"]);
1044     EXPECT_WK_STREQ([zipAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].uniqueIdentifier"]);
1045
1046     {
1047         ObserveAttachmentUpdatesForScope observer(webView.get());
1048         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1049         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1050         NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
1051         EXPECT_EQ(3U, removedAttachments.count);
1052         EXPECT_TRUE([removedAttachments containsObject:zipAttachment.get()]);
1053         EXPECT_TRUE([removedAttachments containsObject:imageAttachment.get()]);
1054         EXPECT_TRUE([removedAttachments containsObject:pdfAttachment.get()]);
1055     }
1056 }
1057
1058 TEST(WKAttachmentTests, DoNotInsertDataURLImagesAsAttachments)
1059 {
1060     auto webContentSourceView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]);
1061     [webContentSourceView synchronouslyLoadTestPageNamed:@"apple-data-url"];
1062     [webContentSourceView selectAll:nil];
1063     [webContentSourceView _synchronouslyExecuteEditCommand:@"Copy" argument:nil];
1064
1065     auto webView = webViewForTestingAttachments();
1066     {
1067         ObserveAttachmentUpdatesForScope observer(webView.get());
1068         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1069         EXPECT_EQ(0U, observer.observer().inserted.count);
1070     }
1071
1072     EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"Boolean(document.querySelector('attachment'))"].boolValue);
1073     EXPECT_EQ(1990, [webView stringByEvaluatingJavaScript:@"document.querySelector('img').src.length"].integerValue);
1074     EXPECT_WK_STREQ("This is an apple", [webView stringByEvaluatingJavaScript:@"document.body.textContent"]);
1075 }
1076
1077 TEST(WKAttachmentTests, InsertAndRemoveDuplicateAttachment)
1078 {
1079     auto webView = webViewForTestingAttachments();
1080     RetainPtr<NSData> data = testHTMLData();
1081     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
1082     RetainPtr<_WKAttachment> originalAttachment;
1083     RetainPtr<_WKAttachment> pastedAttachment;
1084     {
1085         ObserveAttachmentUpdatesForScope observer(webView.get());
1086         originalAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:@"text/plain"];
1087         EXPECT_EQ(0U, observer.observer().removed.count);
1088         observer.expectAttachmentUpdates(@[ ], @[ originalAttachment.get() ]);
1089     }
1090     [webView selectAll:nil];
1091     [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
1092     [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
1093     {
1094         ObserveAttachmentUpdatesForScope observer(webView.get());
1095         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1096         EXPECT_EQ(0U, observer.observer().removed.count);
1097         EXPECT_EQ(1U, observer.observer().inserted.count);
1098         pastedAttachment = observer.observer().inserted.firstObject;
1099         EXPECT_FALSE([pastedAttachment isEqual:originalAttachment.get()]);
1100     }
1101     {
1102         ObserveAttachmentUpdatesForScope observer(webView.get());
1103         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1104         observer.expectAttachmentUpdates(@[ pastedAttachment.get() ], @[ ]);
1105         [originalAttachment expectRequestedDataToBe:data.get()];
1106     }
1107     {
1108         ObserveAttachmentUpdatesForScope observer(webView.get());
1109         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1110         observer.expectAttachmentUpdates(@[ originalAttachment.get() ], @[ ]);
1111     }
1112
1113     EXPECT_FALSE([originalAttachment isEqual:pastedAttachment.get()]);
1114     EXPECT_TRUE([[originalAttachment info].fileWrapper isEqual:[pastedAttachment info].fileWrapper]);
1115     EXPECT_TRUE([[originalAttachment info].fileWrapper isEqual:fileWrapper.get()]);
1116 }
1117
1118 TEST(WKAttachmentTests, InsertDuplicateAttachmentAndUpdateData)
1119 {
1120     auto webView = webViewForTestingAttachments();
1121     auto originalData = retainPtr(testHTMLData());
1122     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:originalData.get()]);
1123     RetainPtr<_WKAttachment> originalAttachment;
1124     RetainPtr<_WKAttachment> pastedAttachment;
1125     {
1126         ObserveAttachmentUpdatesForScope observer(webView.get());
1127         originalAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:@"text/plain"];
1128         EXPECT_EQ(0U, observer.observer().removed.count);
1129         observer.expectAttachmentUpdates(@[ ], @[ originalAttachment.get() ]);
1130     }
1131     [webView selectAll:nil];
1132     [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
1133     [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
1134     {
1135         ObserveAttachmentUpdatesForScope observer(webView.get());
1136         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1137         EXPECT_EQ(0U, observer.observer().removed.count);
1138         EXPECT_EQ(1U, observer.observer().inserted.count);
1139         pastedAttachment = observer.observer().inserted.firstObject;
1140         EXPECT_FALSE([pastedAttachment isEqual:originalAttachment.get()]);
1141     }
1142     auto updatedData = retainPtr([@"HELLO WORLD" dataUsingEncoding:NSUTF8StringEncoding]);
1143     [originalAttachment synchronouslySetData:updatedData.get() newContentType:nil newFilename:nil error:nil];
1144     [originalAttachment expectRequestedDataToBe:updatedData.get()];
1145     [pastedAttachment expectRequestedDataToBe:originalData.get()];
1146
1147     EXPECT_FALSE([originalAttachment isEqual:pastedAttachment.get()]);
1148     EXPECT_FALSE([[originalAttachment info].fileWrapper isEqual:[pastedAttachment info].fileWrapper]);
1149     EXPECT_FALSE([[originalAttachment info].fileWrapper isEqual:fileWrapper.get()]);
1150 }
1151
1152 TEST(WKAttachmentTests, InjectedBundleReplaceURLsWhenPastingAttributedString)
1153 {
1154     platformCopyRichTextWithMultipleAttachments();
1155
1156     auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]);
1157     [[configuration processPool] _setObject:@{ @"image/png" : @"cid:foo-bar" } forBundleParameter:@"MIMETypeToReplacementURLMap"];
1158     auto webView = webViewForTestingAttachments(CGSizeMake(500, 500), configuration.get());
1159     {
1160         ObserveAttachmentUpdatesForScope observer(webView.get());
1161         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1162         EXPECT_EQ(2U, observer.observer().inserted.count);
1163     }
1164     [webView expectElementTagsInOrder:@[ @"IMG", @"ATTACHMENT", @"ATTACHMENT" ]];
1165     EXPECT_WK_STREQ("cid:foo-bar", [webView valueOfAttribute:@"src" forQuerySelector:@"img"]);
1166     EXPECT_WK_STREQ(@"", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('img')[0].webkitAttachmentIdentifier"]);
1167 }
1168
1169 TEST(WKAttachmentTests, InjectedBundleReplaceURLWhenPastingImage)
1170 {
1171     platformCopyPNG();
1172
1173     NSString *replacementURL = @"cid:foo-bar";
1174     auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]);
1175     [[configuration processPool] _setObject:@{ @"image/tiff" : replacementURL, @"image/png" : replacementURL } forBundleParameter:@"MIMETypeToReplacementURLMap"];
1176     auto webView = webViewForTestingAttachments(CGSizeMake(500, 500), configuration.get());
1177     {
1178         ObserveAttachmentUpdatesForScope observer(webView.get());
1179         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1180         EXPECT_EQ(0U, observer.observer().inserted.count);
1181     }
1182     EXPECT_WK_STREQ("cid:foo-bar", [webView valueOfAttribute:@"src" forQuerySelector:@"img"]);
1183 }
1184
1185 TEST(WKAttachmentTests, InsertAttachmentUsingFileWrapperWithFilePath)
1186 {
1187     auto webView = webViewForTestingAttachments();
1188     auto originalFileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testImageFileURL() options:0 error:nil]);
1189     RetainPtr<_WKAttachment> attachment;
1190     {
1191         ObserveAttachmentUpdatesForScope observer(webView.get());
1192         attachment = [webView synchronouslyInsertAttachmentWithFileWrapper:originalFileWrapper.get() contentType:nil];
1193         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
1194     }
1195
1196     _WKAttachmentInfo *infoBeforeUpdate = [attachment info];
1197     EXPECT_WK_STREQ("image/png", infoBeforeUpdate.contentType);
1198     EXPECT_WK_STREQ("icon.png", infoBeforeUpdate.name);
1199     EXPECT_TRUE([originalFileWrapper isEqual:infoBeforeUpdate.fileWrapper]);
1200     [attachment expectRequestedDataToBe:testImageData()];
1201
1202     auto newFileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testPDFFileURL() options:0 error:nil]);
1203     {
1204         ObserveAttachmentUpdatesForScope observer(webView.get());
1205         [attachment synchronouslySetFileWrapper:newFileWrapper.get() newContentType:nil error:nil];
1206         observer.expectAttachmentUpdates(@[ ], @[ ]);
1207     }
1208
1209     _WKAttachmentInfo *infoAfterUpdate = [attachment info];
1210     EXPECT_WK_STREQ("application/pdf", infoAfterUpdate.contentType);
1211     EXPECT_WK_STREQ("test.pdf", infoAfterUpdate.name);
1212     EXPECT_TRUE([newFileWrapper isEqual:infoAfterUpdate.fileWrapper]);
1213     [attachment expectRequestedDataToBe:testPDFData()];
1214 }
1215
1216 TEST(WKAttachmentTests, InvalidateAttachmentsAfterMainFrameNavigation)
1217 {
1218     auto webView = webViewForTestingAttachments();
1219     RetainPtr<_WKAttachment> pdfAttachment;
1220     RetainPtr<_WKAttachment> htmlAttachment;
1221     {
1222         ObserveAttachmentUpdatesForScope insertionObserver(webView.get());
1223         pdfAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"attachment.pdf" contentType:@"application/pdf" data:testPDFData()];
1224         htmlAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"index.html" contentType:@"text/html" data:testHTMLData()];
1225         insertionObserver.expectAttachmentUpdates(@[ ], @[ pdfAttachment.get(), htmlAttachment.get() ]);
1226         EXPECT_TRUE([pdfAttachment isConnected]);
1227         EXPECT_TRUE([htmlAttachment isConnected]);
1228     }
1229
1230     ObserveAttachmentUpdatesForScope removalObserver(webView.get());
1231     [webView synchronouslyLoadTestPageNamed:@"simple"];
1232     removalObserver.expectAttachmentUpdates(@[ pdfAttachment.get(), htmlAttachment.get() ], @[ ]);
1233     EXPECT_FALSE([pdfAttachment isConnected]);
1234     EXPECT_FALSE([htmlAttachment isConnected]);
1235     [pdfAttachment expectRequestedDataToBe:nil];
1236     [htmlAttachment expectRequestedDataToBe:nil];
1237 }
1238
1239 TEST(WKAttachmentTests, InvalidateAttachmentsAfterWebProcessTermination)
1240 {
1241     auto webView = webViewForTestingAttachments();
1242     RetainPtr<_WKAttachment> pdfAttachment;
1243     RetainPtr<_WKAttachment> htmlAttachment;
1244     {
1245         ObserveAttachmentUpdatesForScope insertionObserver(webView.get());
1246         pdfAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"attachment.pdf" contentType:@"application/pdf" data:testPDFData()];
1247         htmlAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"index.html" contentType:@"text/html" data:testHTMLData()];
1248         insertionObserver.expectAttachmentUpdates(@[ ], @[ pdfAttachment.get(), htmlAttachment.get() ]);
1249         EXPECT_TRUE([pdfAttachment isConnected]);
1250         EXPECT_TRUE([htmlAttachment isConnected]);
1251     }
1252     {
1253         ObserveAttachmentUpdatesForScope removalObserver(webView.get());
1254         [webView stringByEvaluatingJavaScript:@"getSelection().collapseToEnd()"];
1255         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1256         removalObserver.expectAttachmentUpdates(@[ htmlAttachment.get() ], @[ ]);
1257         EXPECT_TRUE([pdfAttachment isConnected]);
1258         EXPECT_FALSE([htmlAttachment isConnected]);
1259         [htmlAttachment expectRequestedDataToBe:testHTMLData()];
1260     }
1261
1262     __block bool webProcessTerminated = false;
1263     auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
1264     [webView setNavigationDelegate:navigationDelegate.get()];
1265     [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) {
1266         webProcessTerminated = true;
1267     }];
1268
1269     ObserveAttachmentUpdatesForScope observer(webView.get());
1270     [webView _killWebContentProcess];
1271     TestWebKitAPI::Util::run(&webProcessTerminated);
1272
1273     observer.expectAttachmentUpdates(@[ pdfAttachment.get() ], @[ ]);
1274     EXPECT_FALSE([pdfAttachment isConnected]);
1275     EXPECT_FALSE([htmlAttachment isConnected]);
1276     [pdfAttachment expectRequestedDataToBe:nil];
1277     [htmlAttachment expectRequestedDataToBe:nil];
1278 }
1279
1280 TEST(WKAttachmentTests, MoveAttachmentElementAsIconByDragging)
1281 {
1282     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1283     [configuration _setAttachmentElementEnabled:YES];
1284     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
1285     TestWKWebView *webView = [simulator webView];
1286     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
1287
1288     auto data = retainPtr(testPDFData());
1289     auto attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"document.pdf" contentType:@"application/pdf" data:data.get()]);
1290
1291     [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
1292     [webView _executeEditCommand:@"InsertHTML" argument:@"<strong>text</strong>" completion:nil];
1293     [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
1294     [webView expectElementTag:@"ATTACHMENT" toComeBefore:@"STRONG"];
1295
1296     // Drag the attachment element to somewhere below the strong text.
1297     [simulator runFrom:[webView attachmentElementMidPoint] to:CGPointMake(50, 300)];
1298
1299     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1300     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1301     [attachment expectRequestedDataToBe:data.get()];
1302     EXPECT_EQ([simulator insertedAttachments].count, [simulator removedAttachments].count);
1303 #if PLATFORM(MAC)
1304     EXPECT_FALSE(isCompletelyTransparent([simulator draggingInfo].draggedImage));
1305 #endif
1306
1307     [webView expectElementTag:@"STRONG" toComeBefore:@"ATTACHMENT"];
1308     [simulator endDataTransfer];
1309 }
1310
1311 TEST(WKAttachmentTests, PasteWebArchiveContainingImages)
1312 {
1313     NSData *markupData = [@"<img src='1.png' alt='foo'><div><br></div><img src='2.gif' alt='bar'>" dataUsingEncoding:NSUTF8StringEncoding];
1314
1315     auto mainResource = adoptNS([[WebResource alloc] initWithData:markupData URL:[NSURL URLWithString:@"foo.html"] MIMEType:@"text/html" textEncodingName:@"utf-8" frameName:nil]);
1316     auto pngResource = adoptNS([[WebResource alloc] initWithData:testImageData() URL:[NSURL URLWithString:@"1.png"] MIMEType:@"image/png" textEncodingName:nil frameName:nil]);
1317     auto gifResource = adoptNS([[WebResource alloc] initWithData:testGIFData() URL:[NSURL URLWithString:@"2.gif"] MIMEType:@"image/gif" textEncodingName:nil frameName:nil]);
1318     auto archive = adoptNS([[WebArchive alloc] initWithMainResource:mainResource.get() subresources:@[ pngResource.get(), gifResource.get() ] subframeArchives:@[ ]]);
1319
1320 #if PLATFORM(MAC)
1321     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1322     [pasteboard declareTypes:@[WebArchivePboardType] owner:nil];
1323     [pasteboard setData:[archive data] forType:WebArchivePboardType];
1324 #else
1325     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1326     [pasteboard setData:[archive data] forPasteboardType:WebArchivePboardType];
1327 #endif
1328
1329     RetainPtr<_WKAttachment> gifAttachment;
1330     RetainPtr<_WKAttachment> pngAttachment;
1331     auto webView = webViewForTestingAttachments();
1332
1333     ObserveAttachmentUpdatesForScope observer(webView.get());
1334     [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1335     [webView expectElementCount:2 tagName:@"IMG"];
1336
1337     for (_WKAttachment *attachment in observer.observer().inserted) {
1338         if ([attachment.info.contentType isEqualToString:@"image/png"])
1339             pngAttachment = attachment;
1340         else if ([attachment.info.contentType isEqualToString:@"image/gif"])
1341             gifAttachment = attachment;
1342     }
1343
1344     EXPECT_WK_STREQ("foo", [pngAttachment info].name);
1345     EXPECT_WK_STREQ("bar", [gifAttachment info].name);
1346     [pngAttachment expectRequestedDataToBe:testImageData()];
1347     [gifAttachment expectRequestedDataToBe:testGIFData()];
1348     observer.expectAttachmentUpdates(@[ ], @[ pngAttachment.get(), gifAttachment.get() ]);
1349 }
1350
1351 #pragma mark - Platform-specific tests
1352
1353 #if PLATFORM(MAC)
1354
1355 TEST(WKAttachmentTestsMac, InsertPastedFileURLsAsAttachments)
1356 {
1357     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1358     [pasteboard clearContents];
1359     [pasteboard declareTypes:@[NSFilenamesPboardType] owner:nil];
1360     [pasteboard setPropertyList:@[testPDFFileURL().path, testImageFileURL().path] forType:NSFilenamesPboardType];
1361
1362     RetainPtr<NSArray<_WKAttachment *>> insertedAttachments;
1363     auto webView = webViewForTestingAttachments();
1364     {
1365         ObserveAttachmentUpdatesForScope observer(webView.get());
1366         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1367         insertedAttachments = [observer.observer() inserted];
1368         EXPECT_EQ(2U, [insertedAttachments count]);
1369     }
1370
1371     [webView expectElementCount:1 tagName:@"ATTACHMENT"];
1372     [webView expectElementCount:1 tagName:@"IMG"];
1373     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').getAttribute('type')"]);
1374     EXPECT_WK_STREQ("test.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').getAttribute('title')"]);
1375
1376     NSString *imageAttachmentIdentifier = [webView stringByEvaluatingJavaScript:@"document.querySelector('img').webkitAttachmentIdentifier"];
1377     if ([testImageData() isEqualToData:[insertedAttachments firstObject].info.data])
1378         EXPECT_WK_STREQ([insertedAttachments firstObject].uniqueIdentifier, imageAttachmentIdentifier);
1379     else
1380         EXPECT_WK_STREQ([insertedAttachments lastObject].uniqueIdentifier, imageAttachmentIdentifier);
1381
1382     for (_WKAttachment *attachment in insertedAttachments.get())
1383         EXPECT_GT(attachment.info.filePath.length, 0U);
1384
1385     {
1386         ObserveAttachmentUpdatesForScope observer(webView.get());
1387         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1388         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1389         NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
1390         EXPECT_EQ(2U, removedAttachments.count);
1391         EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments firstObject]]);
1392         EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments lastObject]]);
1393     }
1394 }
1395
1396 TEST(WKAttachmentTestsMac, InsertDroppedFilePromisesAsAttachments)
1397 {
1398     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1399     [configuration _setAttachmentElementEnabled:YES];
1400     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
1401     TestWKWebView *webView = [simulator webView];
1402     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
1403     [simulator writePromisedFiles:@[ testPDFFileURL(), testImageFileURL() ]];
1404
1405     [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(50, 50)];
1406
1407     [webView expectElementCount:1 tagName:@"ATTACHMENT"];
1408     [webView expectElementCount:1 tagName:@"IMG"];
1409     EXPECT_EQ(2U, [simulator insertedAttachments].count);
1410
1411     auto insertedAttachments = retainPtr([simulator insertedAttachments]);
1412     NSArray<NSData *> *expectedData = @[ testPDFData(), testImageData() ];
1413     for (_WKAttachment *attachment in insertedAttachments.get()) {
1414         EXPECT_GT(attachment.info.filePath.length, 0U);
1415         EXPECT_TRUE([expectedData containsObject:attachment.info.data]);
1416         if ([testPDFData() isEqualToData:attachment.info.data])
1417             EXPECT_WK_STREQ("application/pdf", attachment.info.contentType);
1418         else if ([testImageData() isEqualToData:attachment.info.data]) {
1419             EXPECT_WK_STREQ("image/png", attachment.info.contentType);
1420             EXPECT_WK_STREQ(attachment.uniqueIdentifier, [webView stringByEvaluatingJavaScript:@"document.querySelector('img').webkitAttachmentIdentifier"]);
1421         }
1422     }
1423
1424     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1425     [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1426     auto removedAttachments = retainPtr([simulator removedAttachments]);
1427     EXPECT_EQ(2U, [removedAttachments count]);
1428     [webView expectElementCount:0 tagName:@"ATTACHMENT"];
1429     [webView expectElementCount:0 tagName:@"IMG"];
1430     EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments firstObject]]);
1431     EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments lastObject]]);
1432 }
1433
1434 TEST(WKAttachmentTestsMac, DragAttachmentAsFilePromise)
1435 {
1436     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1437     [configuration _setAttachmentElementEnabled:YES];
1438     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
1439     TestWKWebView *webView = [simulator webView];
1440     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
1441
1442     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testPDFFileURL() options:0 error:nil]);
1443     auto attachment = retainPtr([webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:nil]);
1444     [simulator runFrom:[webView attachmentElementMidPoint] to:CGPointMake(300, 300)];
1445
1446     NSArray<NSURL *> *urls = [simulator receivePromisedFiles];
1447     EXPECT_EQ(1U, urls.count);
1448     EXPECT_WK_STREQ("test.pdf", urls.lastObject.lastPathComponent);
1449     EXPECT_TRUE([[NSData dataWithContentsOfURL:urls.firstObject] isEqualToData:testPDFData()]);
1450     EXPECT_FALSE(isCompletelyTransparent([simulator draggingInfo].draggedImage));
1451 }
1452
1453 #endif // PLATFORM(MAC)
1454
1455 #if PLATFORM(IOS)
1456
1457 TEST(WKAttachmentTestsIOS, InsertDroppedImageAsAttachment)
1458 {
1459     auto webView = webViewForTestingAttachments();
1460     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1461     auto item = adoptNS([[NSItemProvider alloc] init]);
1462     [item registerData:testImageData() type:(__bridge NSString *)kUTTypePNG];
1463     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1464     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1465
1466     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1467     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1468     auto attachment = retainPtr([dragAndDropSimulator insertedAttachments].firstObject);
1469     [attachment expectRequestedDataToBe:testImageData()];
1470     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').webkitAttachmentIdentifier"]);
1471
1472     {
1473         ObserveAttachmentUpdatesForScope observer(webView.get());
1474         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1475         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1476         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
1477     }
1478 }
1479
1480 TEST(WKAttachmentTestsIOS, InsertDroppedAttributedStringContainingAttachment)
1481 {
1482     auto webView = webViewForTestingAttachments();
1483     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1484     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(__bridge NSString *)kUTTypePNG]);
1485     auto item = adoptNS([[NSItemProvider alloc] init]);
1486     [item registerObject:[NSAttributedString attributedStringWithAttachment:image.get()] visibility:NSItemProviderRepresentationVisibilityAll];
1487
1488     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1489     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1490
1491     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1492     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1493     auto attachment = retainPtr([dragAndDropSimulator insertedAttachments].firstObject);
1494
1495     auto size = platformImageWithData([attachment info].data).size;
1496     EXPECT_EQ(215., size.width);
1497     EXPECT_EQ(174., size.height);
1498     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').webkitAttachmentIdentifier"]);
1499
1500     {
1501         ObserveAttachmentUpdatesForScope observer(webView.get());
1502         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1503         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1504         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
1505     }
1506 }
1507
1508 TEST(WKAttachmentTestsIOS, InsertDroppedRichAndPlainTextFilesAsAttachments)
1509 {
1510     // Here, both rich text and plain text are content types that WebKit already understands how to insert in editable
1511     // areas in the absence of attachment elements. However, due to the explicitly set attachment presentation style
1512     // on the item providers, we should instead treat them as dropped files and insert attachment elements.
1513     // This exercises the scenario of dragging rich and plain text files from Files to Mail.
1514     auto richTextItem = adoptNS([[NSItemProvider alloc] init]);
1515     auto richText = adoptNS([[NSAttributedString alloc] initWithString:@"Hello world" attributes:@{ NSFontAttributeName: [UIFont boldSystemFontOfSize:12] }]);
1516     [richTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1517     [richTextItem registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
1518     [richTextItem setSuggestedName:@"hello.rtf"];
1519
1520     auto plainTextItem = adoptNS([[NSItemProvider alloc] init]);
1521     [plainTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1522     [plainTextItem registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll];
1523     [plainTextItem setSuggestedName:@"world.txt"];
1524
1525     auto webView = webViewForTestingAttachments();
1526     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1527     [dragAndDropSimulator setExternalItemProviders:@[ richTextItem.get(), plainTextItem.get() ]];
1528     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1529
1530     EXPECT_EQ(2U, [dragAndDropSimulator insertedAttachments].count);
1531     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1532
1533     for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments])
1534         EXPECT_GT([attachment info].data.length, 0U);
1535
1536     [webView expectElementCount:2 tagName:@"ATTACHMENT"];
1537     EXPECT_WK_STREQ("hello.rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
1538     EXPECT_WK_STREQ("text/rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1539     EXPECT_WK_STREQ("world.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
1540     EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
1541 }
1542
1543 TEST(WKAttachmentTestsIOS, InsertDroppedZipArchiveAsAttachment)
1544 {
1545     // Since WebKit doesn't have any default DOM representation for ZIP archives, we should fall back to inserting
1546     // attachment elements. This exercises the flow of dragging a ZIP file from an app that doesn't specify a preferred
1547     // presentation style (e.g. Notes) into Mail.
1548     auto item = adoptNS([[NSItemProvider alloc] init]);
1549     NSData *data = testZIPData();
1550     [item registerData:data type:(__bridge NSString *)kUTTypeZipArchive];
1551     [item setSuggestedName:@"archive.zip"];
1552
1553     auto webView = webViewForTestingAttachments();
1554     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1555     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1556     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1557
1558     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1559     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1560     [[dragAndDropSimulator insertedAttachments].firstObject expectRequestedDataToBe:data];
1561     [webView expectElementCount:1 tagName:@"ATTACHMENT"];
1562     EXPECT_WK_STREQ("archive.zip", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1563     EXPECT_WK_STREQ("application/zip", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1564 }
1565
1566 TEST(WKAttachmentTestsIOS, InsertDroppedItemProvidersInOrder)
1567 {
1568     // Tests that item providers are inserted in the order they are specified. In this case, the two inserted attachments
1569     // should be separated by a link.
1570     auto firstAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
1571     [firstAttachmentItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1572     [firstAttachmentItem registerObject:@"FIRST" visibility:NSItemProviderRepresentationVisibilityAll];
1573     [firstAttachmentItem setSuggestedName:@"first.txt"];
1574
1575     auto inlineTextItem = adoptNS([[NSItemProvider alloc] init]);
1576     auto appleURL = retainPtr([NSURL URLWithString:@"https://www.apple.com/"]);
1577     [inlineTextItem registerObject:appleURL.get() visibility:NSItemProviderRepresentationVisibilityAll];
1578
1579     auto secondAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
1580     [secondAttachmentItem registerData:testPDFData() type:(__bridge NSString *)kUTTypePDF];
1581     [secondAttachmentItem setSuggestedName:@"second.pdf"];
1582
1583     auto webView = webViewForTestingAttachments();
1584     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1585     [dragAndDropSimulator setExternalItemProviders:@[ firstAttachmentItem.get(), inlineTextItem.get(), secondAttachmentItem.get() ]];
1586     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1587
1588     EXPECT_EQ(2U, [dragAndDropSimulator insertedAttachments].count);
1589     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1590
1591     for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments])
1592         EXPECT_GT([attachment info].data.length, 0U);
1593
1594     [webView expectElementTagsInOrder:@[ @"ATTACHMENT", @"A", @"ATTACHMENT" ]];
1595
1596     EXPECT_WK_STREQ("first.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
1597     EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1598     EXPECT_WK_STREQ([appleURL absoluteString], [webView valueOfAttribute:@"href" forQuerySelector:@"a"]);
1599     EXPECT_WK_STREQ("second.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
1600     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
1601 }
1602
1603 TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsFile)
1604 {
1605     auto item = adoptNS([[NSItemProvider alloc] init]);
1606     auto data = retainPtr(testPDFData());
1607     [item registerData:data.get() type:(__bridge NSString *)kUTTypePDF];
1608     [item setSuggestedName:@"document.pdf"];
1609
1610     auto webView = webViewForTestingAttachments();
1611     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1612     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1613     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1614
1615     // First, verify that the attachment was successfully dropped.
1616     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1617     _WKAttachment *attachment = [dragAndDropSimulator insertedAttachments].firstObject;
1618     [attachment expectRequestedDataToBe:data.get()];
1619     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1620     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1621
1622     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
1623     [dragAndDropSimulator setExternalItemProviders:@[ ]];
1624     [dragAndDropSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
1625
1626     // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
1627     EXPECT_EQ(1U, [dragAndDropSimulator sourceItemProviders].count);
1628     NSItemProvider *itemProvider = [dragAndDropSimulator sourceItemProviders].firstObject;
1629     EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
1630     [itemProvider expectType:(__bridge NSString *)kUTTypePDF withData:data.get()];
1631     EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
1632     [dragAndDropSimulator endDataTransfer];
1633 }
1634
1635 TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsData)
1636 {
1637     auto webView = webViewForTestingAttachments();
1638     auto data = retainPtr(testPDFData());
1639     RetainPtr<_WKAttachment> attachment;
1640     {
1641         ObserveAttachmentUpdatesForScope observer(webView.get());
1642         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"document.pdf" contentType:@"application/pdf" data:data.get()];
1643         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
1644     }
1645
1646     // First, verify that the attachment was successfully inserted from raw data.
1647     [attachment expectRequestedDataToBe:data.get()];
1648     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1649     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1650
1651     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
1652     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1653     [dragAndDropSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
1654
1655     // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
1656     EXPECT_EQ(1U, [dragAndDropSimulator sourceItemProviders].count);
1657     NSItemProvider *itemProvider = [dragAndDropSimulator sourceItemProviders].firstObject;
1658     EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
1659     [itemProvider expectType:(__bridge NSString *)kUTTypePDF withData:data.get()];
1660     EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
1661     [dragAndDropSimulator endDataTransfer];
1662 }
1663
1664 #endif // PLATFORM(IOS)
1665
1666 } // namespace TestWebKitAPI
1667
1668 #endif // WK_API_ENABLED && !PLATFORM(WATCHOS) && !PLATFORM(TVOS)