[iOS] Remove all usages of UIItemProvider, UIItemProviderReading, and related classes
[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_FAMILY)
40 #import <MobileCoreServices/MobileCoreServices.h>
41 #endif
42
43 #define USES_MODERN_ATTRIBUTED_STRING_CONVERSION ((PLATFORM(IOS_FAMILY) && __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((__bridge 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 - (_WKAttachment *)synchronouslyInsertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType
250 {
251     __block bool done = false;
252     RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFileWrapper:fileWrapper contentType:contentType completion:^(BOOL) {
253         done = true;
254     }];
255     TestWebKitAPI::Util::run(&done);
256     return attachment.autorelease();
257 }
258
259 - (_WKAttachment *)synchronouslyInsertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data
260 {
261     __block bool done = false;
262     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
263     if (filename)
264         [fileWrapper setPreferredFilename:filename];
265     RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFileWrapper:fileWrapper.get() contentType:contentType completion:^(BOOL) {
266         done = true;
267     }];
268     TestWebKitAPI::Util::run(&done);
269     return attachment.autorelease();
270 }
271
272 - (CGPoint)attachmentElementMidPoint
273 {
274     __block CGPoint midPoint;
275     __block bool doneEvaluatingScript = false;
276     [self evaluateJavaScript:@"r = document.querySelector('attachment').getBoundingClientRect(); [r.left + r.width / 2, r.top + r.height / 2]" completionHandler:^(NSArray<NSNumber *> *result, NSError *) {
277         midPoint = CGPointMake(result.firstObject.floatValue, result.lastObject.floatValue);
278         doneEvaluatingScript = true;
279     }];
280     TestWebKitAPI::Util::run(&doneEvaluatingScript);
281     return midPoint;
282 }
283
284 - (CGSize)attachmentElementSize
285 {
286     __block CGSize size;
287     __block bool doneEvaluatingScript = false;
288     [self evaluateJavaScript:@"r = document.querySelector('attachment').getBoundingClientRect(); [r.width, r.height]" completionHandler:^(NSArray<NSNumber *> *sizeResult, NSError *) {
289         size = CGSizeMake(sizeResult.firstObject.floatValue, sizeResult.lastObject.floatValue);
290         doneEvaluatingScript = true;
291     }];
292     TestWebKitAPI::Util::run(&doneEvaluatingScript);
293     return size;
294 }
295
296 - (CGSize)imageElementSize
297 {
298     __block CGSize size;
299     __block bool doneEvaluatingScript = false;
300     [self evaluateJavaScript:@"r = document.querySelector('img').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)waitForImageElementSizeToBecome:(CGSize)expectedSize
309 {
310     while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]) {
311         if (CGSizeEqualToSize(self.imageElementSize, 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:[protectedFolderURL = retainPtr(folderURL)] (void(^completion)(NSURL *, BOOL, NSError *)) -> NSProgress * {
420         completion(protectedFolderURL.get(), 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_FAMILY)
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_FAMILY)
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_FAMILY)
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_FAMILY)
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_FAMILY)
534     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
535     auto item = adoptNS([[NSItemProvider 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 @interface FileWrapper : NSFileWrapper
558 @end
559
560 @implementation FileWrapper
561 @end
562
563 namespace TestWebKitAPI {
564
565 #pragma mark - Platform-agnostic tests
566
567 TEST(WKAttachmentTests, AttachmentElementInsertion)
568 {
569     auto webView = webViewForTestingAttachments();
570     RetainPtr<_WKAttachment> firstAttachment;
571     RetainPtr<_WKAttachment> secondAttachment;
572     {
573         ObserveAttachmentUpdatesForScope observer(webView.get());
574         // Use the given content type for the attachment element's type.
575         firstAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo" contentType:@"text/html" data:testHTMLData()];
576         EXPECT_WK_STREQ(@"foo", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
577         EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
578         EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
579         observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
580     }
581
582     _WKAttachmentInfo *info = [firstAttachment info];
583     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
584     EXPECT_TRUE([info.contentType isEqualToString:@"text/html"]);
585     EXPECT_TRUE([info.name isEqualToString:@"foo"]);
586     EXPECT_EQ(info.filePath.length, 0U);
587
588     {
589         ObserveAttachmentUpdatesForScope scope(webView.get());
590         // Since no content type is explicitly specified, compute it from the file extension.
591         [webView _executeEditCommand:@"DeleteBackward" argument:nil completion:nil];
592         secondAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"bar.png" contentType:nil data:testImageData()];
593         EXPECT_WK_STREQ(@"bar.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
594         EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
595         EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
596         scope.expectAttachmentUpdates(@[ firstAttachment.get() ], @[ secondAttachment.get() ]);
597     }
598
599     [firstAttachment expectRequestedDataToBe:testHTMLData()];
600     [secondAttachment expectRequestedDataToBe:testImageData()];
601     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
602     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
603     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
604 }
605
606 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingAndDeletingNewline)
607 {
608     auto webView = webViewForTestingAttachments();
609     RetainPtr<_WKAttachment> attachment;
610     {
611         ObserveAttachmentUpdatesForScope observer(webView.get());
612         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
613         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
614     }
615     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
616     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
617     [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body)"];
618     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
619
620     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
621
622     _WKAttachmentInfo *info = [attachment info];
623     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
624     EXPECT_TRUE([info.contentType isEqualToString:@"text/plain"]);
625     EXPECT_TRUE([info.name isEqualToString:@"foo.txt"]);
626     EXPECT_EQ(info.filePath.length, 0U);
627
628     [webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
629     [attachment expectRequestedDataToBe:testHTMLData()];
630 }
631
632 TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing)
633 {
634     auto webView = webViewForTestingAttachments();
635     RetainPtr<NSData> htmlData = testHTMLData();
636     RetainPtr<_WKAttachment> attachment;
637     {
638         ObserveAttachmentUpdatesForScope observer(webView.get());
639         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
640         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
641     }
642     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
643     [attachment expectRequestedDataToBe:htmlData.get()];
644
645     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
646     [attachment expectRequestedDataToBe:htmlData.get()];
647
648     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
649     [attachment expectRequestedDataToBe:htmlData.get()];
650
651     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
652     [attachment expectRequestedDataToBe:htmlData.get()];
653
654     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
655     [attachment expectRequestedDataToBe:htmlData.get()];
656 }
657
658 TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles)
659 {
660     auto webView = webViewForTestingAttachments();
661     RetainPtr<_WKAttachment> attachment;
662     [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello"];
663     {
664         ObserveAttachmentUpdatesForScope observer(webView.get());
665         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
666         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
667     }
668     [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"World" expectedRemovals:@[] expectedInsertions:@[]];
669     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
670     [webView expectUpdatesAfterCommand:@"ToggleBold" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
671     [webView expectUpdatesAfterCommand:@"ToggleItalic" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
672     [webView expectUpdatesAfterCommand:@"ToggleUnderline" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
673     [attachment expectRequestedDataToBe:testHTMLData()];
674     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
675     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
676     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
677
678     // Inserting text should delete the current selection, removing the attachment in the process.
679     [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"foo" expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
680     [attachment expectRequestedDataToBe:testHTMLData()];
681 }
682
683 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingLists)
684 {
685     auto webView = webViewForTestingAttachments();
686     RetainPtr<_WKAttachment> attachment;
687     {
688         ObserveAttachmentUpdatesForScope observer(webView.get());
689         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
690         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
691     }
692     [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
693     // This edit command behaves more like a "toggle", and will actually break us out of the list we just inserted.
694     [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
695     [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
696     [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
697     [attachment expectRequestedDataToBe:testHTMLData()];
698     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
699     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
700     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
701 }
702
703 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingRichMarkup)
704 {
705     auto webView = webViewForTestingAttachments();
706     RetainPtr<_WKAttachment> attachment;
707     {
708         ObserveAttachmentUpdatesForScope observer(webView.get());
709         [webView _synchronouslyExecuteEditCommand:@"InsertHTML" argument:@"<div><strong><attachment src='cid:123-4567' title='a'></attachment></strong></div>"];
710         attachment = observer.observer().inserted[0];
711         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
712         observer.expectSourceForIdentifier(@"cid:123-4567", [attachment uniqueIdentifier]);
713     }
714     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
715     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
716     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
717     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
718     {
719         ObserveAttachmentUpdatesForScope observer(webView.get());
720         [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').remove()"];
721         [webView waitForNextPresentationUpdate];
722         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
723     }
724     [attachment expectRequestedDataToBe:nil];
725 }
726
727 TEST(WKAttachmentTests, AttachmentUpdatesWhenCuttingAndPasting)
728 {
729     auto webView = webViewForTestingAttachments();
730     RetainPtr<_WKAttachment> attachment;
731     {
732         ObserveAttachmentUpdatesForScope observer(webView.get());
733         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData()];
734         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
735     }
736     [attachment expectRequestedDataToBe:testHTMLData()];
737     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
738     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
739     {
740         ObserveAttachmentUpdatesForScope observer(webView.get());
741         [webView _synchronouslyExecuteEditCommand:@"Cut" argument:nil];
742         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
743     }
744     [attachment expectRequestedDataToBe:testHTMLData()];
745     {
746         ObserveAttachmentUpdatesForScope observer(webView.get());
747         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
748         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
749     }
750     [attachment expectRequestedDataToBe:testHTMLData()];
751     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
752     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
753     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
754 }
755
756 TEST(WKAttachmentTests, AttachmentDataForEmptyFile)
757 {
758     auto webView = webViewForTestingAttachments();
759     RetainPtr<_WKAttachment> attachment;
760     {
761         ObserveAttachmentUpdatesForScope observer(webView.get());
762         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"empty.txt" contentType:@"text/plain" data:[NSData data]];
763         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
764     }
765     [attachment expectRequestedDataToBe:[NSData data]];
766     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
767     {
768         ObserveAttachmentUpdatesForScope scope(webView.get());
769         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
770         scope.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
771     }
772     [attachment expectRequestedDataToBe:[NSData data]];
773 }
774
775 TEST(WKAttachmentTests, DropFolderAsAttachmentAndMoveByDragging)
776 {
777     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
778     [configuration _setAttachmentElementEnabled:YES];
779
780     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
781     [[simulator webView] synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
782
783     runTestWithTemporaryFolder([simulator] (NSURL *folderURL) {
784         simulateFolderDragWithURL(simulator.get(), folderURL);
785         [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(50, 50)];
786
787         TestWKWebView *webView = [simulator webView];
788         auto attachment = retainPtr([simulator insertedAttachments].firstObject);
789         EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
790         EXPECT_WK_STREQ((__bridge NSString *)kUTTypeDirectory, [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
791         EXPECT_WK_STREQ(folderURL.lastPathComponent, [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
792
793         NSFileWrapper *image = [attachment info].fileWrapper.fileWrappers[@"image.png"];
794         NSFileWrapper *archive = [attachment info].fileWrapper.fileWrappers[@"archive.zip"];
795         EXPECT_TRUE([image.regularFileContents isEqualToData:testImageData()]);
796         EXPECT_TRUE([archive.regularFileContents isEqualToData:testZIPData()]);
797
798         [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
799         [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
800         [webView _executeEditCommand:@"InsertHTML" argument:@"<em>foo</em>" completion:nil];
801         [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
802
803         [webView expectElementTag:@"ATTACHMENT" toComeBefore:@"EM"];
804         [simulator clearExternalDragInformation];
805         [simulator runFrom:webView.attachmentElementMidPoint to:CGPointMake(300, 300)];
806         [webView expectElementTag:@"EM" toComeBefore:@"ATTACHMENT"];
807     });
808 }
809
810 TEST(WKAttachmentTests, InsertFolderAndFileWithUnknownExtension)
811 {
812     auto webView = webViewForTestingAttachments();
813     auto file = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testHTMLData()]);
814     [file setPreferredFilename:@"test.foobar"];
815     auto image = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testImageData()]);
816     auto document = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testPDFData()]);
817     auto folder = adoptNS([[NSFileWrapper alloc] initDirectoryWithFileWrappers:@{ @"image.png": image.get(), @"document.pdf": document.get() }]);
818     [folder setPreferredFilename:@"folder"];
819
820     RetainPtr<_WKAttachment> firstAttachment;
821     RetainPtr<_WKAttachment> secondAttachment;
822     {
823         ObserveAttachmentUpdatesForScope observer(webView.get());
824         firstAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:file.get() contentType:nil];
825         observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
826     }
827     {
828         ObserveAttachmentUpdatesForScope observer(webView.get());
829         secondAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:folder.get() contentType:nil];
830         observer.expectAttachmentUpdates(@[ ], @[ secondAttachment.get() ]);
831     }
832
833     auto checkAttachmentConsistency = [webView, file, folder] (_WKAttachment *expectedFileAttachment, _WKAttachment *expectedFolderAttachment) {
834         [webView expectElementCount:2 tagName:@"ATTACHMENT"];
835         EXPECT_TRUE(UTTypeConformsTo((__bridge CFStringRef)[webView valueOfAttribute:@"type" forQuerySelector:@"attachment[title=folder]"], kUTTypeDirectory));
836         EXPECT_TRUE(UTTypeConformsTo((__bridge CFStringRef)[webView valueOfAttribute:@"type" forQuerySelector:@"attachment[title^=test]"], kUTTypeData));
837         EXPECT_WK_STREQ(expectedFileAttachment.uniqueIdentifier, [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment[title^=test]').uniqueIdentifier"]);
838         EXPECT_WK_STREQ(expectedFolderAttachment.uniqueIdentifier, [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment[title=folder]').uniqueIdentifier"]);
839         EXPECT_TRUE([expectedFileAttachment.info.fileWrapper isEqual:file.get()]);
840         EXPECT_TRUE([expectedFolderAttachment.info.fileWrapper isEqual:folder.get()]);
841     };
842
843     checkAttachmentConsistency(firstAttachment.get(), secondAttachment.get());
844
845     {
846         // Swap the two attachments' file wrappers without creating or destroying attachment elements.
847         ObserveAttachmentUpdatesForScope observer(webView.get());
848         [firstAttachment synchronouslySetFileWrapper:folder.get() newContentType:nil error:nil];
849         [secondAttachment synchronouslySetFileWrapper:file.get() newContentType:nil error:nil];
850         observer.expectAttachmentUpdates(@[ ], @[ ]);
851     }
852
853     checkAttachmentConsistency(secondAttachment.get(), firstAttachment.get());
854 }
855
856 TEST(WKAttachmentTests, ChangeAttachmentDataAndFileInformation)
857 {
858     auto webView = webViewForTestingAttachments();
859     RetainPtr<_WKAttachment> attachment;
860     {
861         RetainPtr<NSData> pdfData = testPDFData();
862         ObserveAttachmentUpdatesForScope observer(webView.get());
863         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"test.pdf" contentType:@"application/pdf" data:pdfData.get()];
864         [attachment expectRequestedDataToBe:pdfData.get()];
865         EXPECT_WK_STREQ(@"test.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
866         EXPECT_WK_STREQ(@"application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
867         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
868     }
869     {
870         RetainPtr<NSData> imageData = testImageData();
871         ObserveAttachmentUpdatesForScope observer(webView.get());
872         [attachment synchronouslySetData:imageData.get() newContentType:@"image/png" newFilename:@"icon.png" error:nil];
873         [attachment expectRequestedDataToBe:imageData.get()];
874         EXPECT_WK_STREQ(@"icon.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
875         EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
876         observer.expectAttachmentUpdates(@[ ], @[ ]);
877     }
878     {
879         RetainPtr<NSData> textData = [@"Hello world" dataUsingEncoding:NSUTF8StringEncoding];
880         ObserveAttachmentUpdatesForScope observer(webView.get());
881         // The new content type should be inferred from the file name.
882         [attachment synchronouslySetData:textData.get() newContentType:nil newFilename:@"foo.txt" error:nil];
883         [attachment expectRequestedDataToBe:textData.get()];
884         EXPECT_WK_STREQ(@"foo.txt", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
885         EXPECT_WK_STREQ(@"text/plain", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
886         observer.expectAttachmentUpdates(@[ ], @[ ]);
887     }
888     {
889         RetainPtr<NSData> htmlData = testHTMLData();
890         ObserveAttachmentUpdatesForScope observer(webView.get());
891         [attachment synchronouslySetData:htmlData.get() newContentType:@"text/html" newFilename:@"bar" error:nil];
892         [attachment expectRequestedDataToBe:htmlData.get()];
893         EXPECT_WK_STREQ(@"bar", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
894         EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
895         observer.expectAttachmentUpdates(@[ ], @[ ]);
896     }
897     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[ ]];
898 }
899
900 TEST(WKAttachmentTests, RemoveNewlinesBeforePastedImage)
901 {
902     platformCopyPNG();
903
904     RetainPtr<_WKAttachment> attachment;
905     auto webView = webViewForTestingAttachments();
906     {
907         ObserveAttachmentUpdatesForScope observer(webView.get());
908         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
909         EXPECT_EQ(1U, observer.observer().inserted.count);
910         attachment = observer.observer().inserted[0];
911     }
912
913     auto size = platformImageWithData([attachment info].data).size;
914     EXPECT_EQ(215., size.width);
915     EXPECT_EQ(174., size.height);
916     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"]);
917
918     [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body, 0)"];
919     {
920         ObserveAttachmentUpdatesForScope observer(webView.get());
921         [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
922         [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
923         observer.expectAttachmentUpdates(@[ ], @[ ]);
924         [webView expectElementTagsInOrder:@[ @"BR", @"BR", @"IMG" ]];
925     }
926     {
927         ObserveAttachmentUpdatesForScope observer(webView.get());
928         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
929         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
930         observer.expectAttachmentUpdates(@[ ], @[ ]);
931         [webView expectElementCount:0 tagName:@"BR"];
932     }
933 }
934
935 TEST(WKAttachmentTests, CutAndPastePastedImage)
936 {
937     platformCopyPNG();
938
939     RetainPtr<_WKAttachment> attachment;
940     auto webView = webViewForTestingAttachments();
941     {
942         ObserveAttachmentUpdatesForScope observer(webView.get());
943         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
944         EXPECT_EQ(1U, observer.observer().inserted.count);
945         attachment = observer.observer().inserted[0];
946     }
947     {
948         ObserveAttachmentUpdatesForScope observer(webView.get());
949         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
950         [webView _synchronouslyExecuteEditCommand:@"Cut" argument:nil];
951         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
952     }
953     {
954         ObserveAttachmentUpdatesForScope observer(webView.get());
955         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
956         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
957     }
958 }
959
960 TEST(WKAttachmentTests, MovePastedImageByDragging)
961 {
962     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
963     [configuration _setAttachmentElementEnabled:YES];
964     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
965     TestWKWebView *webView = [simulator webView];
966     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
967
968     platformCopyPNG();
969     [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
970     [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
971     [webView _executeEditCommand:@"InsertHTML" argument:@"<strong>text</strong>" completion:nil];
972     [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
973     [webView expectElementTag:@"IMG" toComeBefore:@"STRONG"];
974     [webView expectElementCount:1 tagName:@"IMG"];
975
976     // Drag the attachment element to somewhere below the strong text.
977     [simulator runFrom:CGPointMake(50, 50) to:CGPointMake(50, 350)];
978
979     [webView expectElementTag:@"STRONG" toComeBefore:@"IMG"];
980     [webView expectElementCount:1 tagName:@"IMG"];
981     EXPECT_EQ([simulator insertedAttachments].count, [simulator removedAttachments].count);
982
983     [simulator endDataTransfer];
984 }
985
986 TEST(WKAttachmentTests, InsertPastedAttributedStringContainingImage)
987 {
988     platformCopyRichTextWithImage();
989
990     RetainPtr<_WKAttachment> attachment;
991     auto webView = webViewForTestingAttachments();
992     {
993         ObserveAttachmentUpdatesForScope observer(webView.get());
994         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
995         EXPECT_EQ(0U, observer.observer().removed.count);
996         EXPECT_EQ(1U, observer.observer().inserted.count);
997         attachment = observer.observer().inserted[0];
998     }
999
1000     [attachment expectRequestedDataToBe:testImageData()];
1001     EXPECT_WK_STREQ("Lorem ipsum  dolor sit amet.", [webView stringByEvaluatingJavaScript:@"document.body.textContent"]);
1002     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"]);
1003
1004     {
1005         ObserveAttachmentUpdatesForScope observer(webView.get());
1006         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1007         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1008         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
1009     }
1010 }
1011
1012 TEST(WKAttachmentTests, InsertPastedAttributedStringContainingMultipleAttachments)
1013 {
1014     platformCopyRichTextWithMultipleAttachments();
1015
1016     RetainPtr<_WKAttachment> imageAttachment;
1017     RetainPtr<_WKAttachment> zipAttachment;
1018     RetainPtr<_WKAttachment> pdfAttachment;
1019     auto webView = webViewForTestingAttachments();
1020     {
1021         ObserveAttachmentUpdatesForScope observer(webView.get());
1022         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1023         EXPECT_EQ(0U, observer.observer().removed.count);
1024         EXPECT_EQ(3U, observer.observer().inserted.count);
1025         for (_WKAttachment *attachment in observer.observer().inserted) {
1026             NSData *data = attachment.info.data;
1027             if ([data isEqualToData:testZIPData()])
1028                 zipAttachment = attachment;
1029             else if ([data isEqualToData:testPDFData()])
1030                 pdfAttachment = attachment;
1031             else if ([data isEqualToData:testImageData()])
1032                 imageAttachment = attachment;
1033         }
1034     }
1035
1036     EXPECT_TRUE(zipAttachment && imageAttachment && pdfAttachment);
1037     [webView expectElementCount:2 tagName:@"ATTACHMENT"];
1038     [webView expectElementCount:1 tagName:@"IMG"];
1039     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1040
1041     NSString *zipAttachmentType = [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"];
1042 #if USES_MODERN_ATTRIBUTED_STRING_CONVERSION
1043     EXPECT_WK_STREQ("application/zip", zipAttachmentType);
1044 #else
1045     EXPECT_WK_STREQ("application/octet-stream", zipAttachmentType);
1046 #endif
1047
1048     EXPECT_WK_STREQ([imageAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"]);
1049     EXPECT_WK_STREQ([pdfAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].uniqueIdentifier"]);
1050     EXPECT_WK_STREQ([zipAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].uniqueIdentifier"]);
1051
1052     {
1053         ObserveAttachmentUpdatesForScope observer(webView.get());
1054         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1055         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1056         NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
1057         EXPECT_EQ(3U, removedAttachments.count);
1058         EXPECT_TRUE([removedAttachments containsObject:zipAttachment.get()]);
1059         EXPECT_TRUE([removedAttachments containsObject:imageAttachment.get()]);
1060         EXPECT_TRUE([removedAttachments containsObject:pdfAttachment.get()]);
1061     }
1062 }
1063
1064 TEST(WKAttachmentTests, DoNotInsertDataURLImagesAsAttachments)
1065 {
1066     auto webContentSourceView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]);
1067     [webContentSourceView synchronouslyLoadTestPageNamed:@"apple-data-url"];
1068     [webContentSourceView selectAll:nil];
1069     [webContentSourceView _synchronouslyExecuteEditCommand:@"Copy" argument:nil];
1070
1071     auto webView = webViewForTestingAttachments();
1072     {
1073         ObserveAttachmentUpdatesForScope observer(webView.get());
1074         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1075         EXPECT_EQ(0U, observer.observer().inserted.count);
1076     }
1077
1078     EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"Boolean(document.querySelector('attachment'))"].boolValue);
1079     EXPECT_EQ(1990, [webView stringByEvaluatingJavaScript:@"document.querySelector('img').src.length"].integerValue);
1080     EXPECT_WK_STREQ("This is an apple", [webView stringByEvaluatingJavaScript:@"document.body.textContent"]);
1081 }
1082
1083 TEST(WKAttachmentTests, InsertAndRemoveDuplicateAttachment)
1084 {
1085     auto webView = webViewForTestingAttachments();
1086     RetainPtr<NSData> data = testHTMLData();
1087     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]);
1088     RetainPtr<_WKAttachment> originalAttachment;
1089     RetainPtr<_WKAttachment> pastedAttachment;
1090     {
1091         ObserveAttachmentUpdatesForScope observer(webView.get());
1092         originalAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:@"text/plain"];
1093         EXPECT_EQ(0U, observer.observer().removed.count);
1094         observer.expectAttachmentUpdates(@[ ], @[ originalAttachment.get() ]);
1095     }
1096     [webView selectAll:nil];
1097     [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
1098     [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
1099     {
1100         ObserveAttachmentUpdatesForScope observer(webView.get());
1101         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1102         EXPECT_EQ(0U, observer.observer().removed.count);
1103         EXPECT_EQ(1U, observer.observer().inserted.count);
1104         pastedAttachment = observer.observer().inserted.firstObject;
1105         EXPECT_FALSE([pastedAttachment isEqual:originalAttachment.get()]);
1106     }
1107     {
1108         ObserveAttachmentUpdatesForScope observer(webView.get());
1109         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1110         observer.expectAttachmentUpdates(@[ pastedAttachment.get() ], @[ ]);
1111         [originalAttachment expectRequestedDataToBe:data.get()];
1112     }
1113     {
1114         ObserveAttachmentUpdatesForScope observer(webView.get());
1115         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1116         observer.expectAttachmentUpdates(@[ originalAttachment.get() ], @[ ]);
1117     }
1118
1119     EXPECT_FALSE([originalAttachment isEqual:pastedAttachment.get()]);
1120     EXPECT_TRUE([[originalAttachment info].fileWrapper isEqual:[pastedAttachment info].fileWrapper]);
1121     EXPECT_TRUE([[originalAttachment info].fileWrapper isEqual:fileWrapper.get()]);
1122 }
1123
1124 TEST(WKAttachmentTests, InsertDuplicateAttachmentAndUpdateData)
1125 {
1126     auto webView = webViewForTestingAttachments();
1127     auto originalData = retainPtr(testHTMLData());
1128     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:originalData.get()]);
1129     RetainPtr<_WKAttachment> originalAttachment;
1130     RetainPtr<_WKAttachment> pastedAttachment;
1131     {
1132         ObserveAttachmentUpdatesForScope observer(webView.get());
1133         originalAttachment = [webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:@"text/plain"];
1134         EXPECT_EQ(0U, observer.observer().removed.count);
1135         observer.expectAttachmentUpdates(@[ ], @[ originalAttachment.get() ]);
1136     }
1137     [webView selectAll:nil];
1138     [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
1139     [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
1140     {
1141         ObserveAttachmentUpdatesForScope observer(webView.get());
1142         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1143         EXPECT_EQ(0U, observer.observer().removed.count);
1144         EXPECT_EQ(1U, observer.observer().inserted.count);
1145         pastedAttachment = observer.observer().inserted.firstObject;
1146         EXPECT_FALSE([pastedAttachment isEqual:originalAttachment.get()]);
1147     }
1148     auto updatedData = retainPtr([@"HELLO WORLD" dataUsingEncoding:NSUTF8StringEncoding]);
1149     [originalAttachment synchronouslySetData:updatedData.get() newContentType:nil newFilename:nil error:nil];
1150     [originalAttachment expectRequestedDataToBe:updatedData.get()];
1151     [pastedAttachment expectRequestedDataToBe:originalData.get()];
1152
1153     EXPECT_FALSE([originalAttachment isEqual:pastedAttachment.get()]);
1154     EXPECT_FALSE([[originalAttachment info].fileWrapper isEqual:[pastedAttachment info].fileWrapper]);
1155     EXPECT_FALSE([[originalAttachment info].fileWrapper isEqual:fileWrapper.get()]);
1156 }
1157
1158 TEST(WKAttachmentTests, InjectedBundleReplaceURLsWhenPastingAttributedString)
1159 {
1160     platformCopyRichTextWithMultipleAttachments();
1161
1162     auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]);
1163     [[configuration processPool] _setObject:@{ @"image/png" : @"cid:foo-bar" } forBundleParameter:@"MIMETypeToReplacementURLMap"];
1164     auto webView = webViewForTestingAttachments(CGSizeMake(500, 500), configuration.get());
1165     {
1166         ObserveAttachmentUpdatesForScope observer(webView.get());
1167         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1168         EXPECT_EQ(2U, observer.observer().inserted.count);
1169     }
1170     [webView expectElementTagsInOrder:@[ @"IMG", @"ATTACHMENT", @"ATTACHMENT" ]];
1171     EXPECT_WK_STREQ("cid:foo-bar", [webView valueOfAttribute:@"src" forQuerySelector:@"img"]);
1172     EXPECT_WK_STREQ(@"", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('img')[0].attachmentIdentifier"]);
1173 }
1174
1175 TEST(WKAttachmentTests, InjectedBundleReplaceURLWhenPastingImage)
1176 {
1177     platformCopyPNG();
1178
1179     NSString *replacementURL = @"cid:foo-bar";
1180     auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BundleEditingDelegatePlugIn"]);
1181     [[configuration processPool] _setObject:@{ @"image/tiff" : replacementURL, @"image/png" : replacementURL } forBundleParameter:@"MIMETypeToReplacementURLMap"];
1182     auto webView = webViewForTestingAttachments(CGSizeMake(500, 500), configuration.get());
1183     {
1184         ObserveAttachmentUpdatesForScope observer(webView.get());
1185         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1186         EXPECT_EQ(0U, observer.observer().inserted.count);
1187     }
1188     EXPECT_WK_STREQ("cid:foo-bar", [webView valueOfAttribute:@"src" forQuerySelector:@"img"]);
1189 }
1190
1191 TEST(WKAttachmentTests, InsertAttachmentUsingFileWrapperWithFilePath)
1192 {
1193     auto webView = webViewForTestingAttachments();
1194     auto originalFileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testImageFileURL() options:0 error:nil]);
1195     RetainPtr<_WKAttachment> attachment;
1196     {
1197         ObserveAttachmentUpdatesForScope observer(webView.get());
1198         attachment = [webView synchronouslyInsertAttachmentWithFileWrapper:originalFileWrapper.get() contentType:nil];
1199         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
1200     }
1201
1202     _WKAttachmentInfo *infoBeforeUpdate = [attachment info];
1203     EXPECT_WK_STREQ("image/png", infoBeforeUpdate.contentType);
1204     EXPECT_WK_STREQ("icon.png", infoBeforeUpdate.name);
1205     EXPECT_TRUE([originalFileWrapper isEqual:infoBeforeUpdate.fileWrapper]);
1206     [attachment expectRequestedDataToBe:testImageData()];
1207
1208     auto newFileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testPDFFileURL() options:0 error:nil]);
1209     {
1210         ObserveAttachmentUpdatesForScope observer(webView.get());
1211         [attachment synchronouslySetFileWrapper:newFileWrapper.get() newContentType:nil error:nil];
1212         observer.expectAttachmentUpdates(@[ ], @[ ]);
1213     }
1214
1215     _WKAttachmentInfo *infoAfterUpdate = [attachment info];
1216     EXPECT_WK_STREQ("application/pdf", infoAfterUpdate.contentType);
1217     EXPECT_WK_STREQ("test.pdf", infoAfterUpdate.name);
1218     EXPECT_TRUE([newFileWrapper isEqual:infoAfterUpdate.fileWrapper]);
1219     [attachment expectRequestedDataToBe:testPDFData()];
1220 }
1221
1222 TEST(WKAttachmentTests, InvalidateAttachmentsAfterMainFrameNavigation)
1223 {
1224     auto webView = webViewForTestingAttachments();
1225     RetainPtr<_WKAttachment> pdfAttachment;
1226     RetainPtr<_WKAttachment> htmlAttachment;
1227     {
1228         ObserveAttachmentUpdatesForScope insertionObserver(webView.get());
1229         pdfAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"attachment.pdf" contentType:@"application/pdf" data:testPDFData()];
1230         htmlAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"index.html" contentType:@"text/html" data:testHTMLData()];
1231         insertionObserver.expectAttachmentUpdates(@[ ], @[ pdfAttachment.get(), htmlAttachment.get() ]);
1232         EXPECT_TRUE([pdfAttachment isConnected]);
1233         EXPECT_TRUE([htmlAttachment isConnected]);
1234     }
1235
1236     ObserveAttachmentUpdatesForScope removalObserver(webView.get());
1237     [webView synchronouslyLoadTestPageNamed:@"simple"];
1238     removalObserver.expectAttachmentUpdates(@[ pdfAttachment.get(), htmlAttachment.get() ], @[ ]);
1239     EXPECT_FALSE([pdfAttachment isConnected]);
1240     EXPECT_FALSE([htmlAttachment isConnected]);
1241     [pdfAttachment expectRequestedDataToBe:nil];
1242     [htmlAttachment expectRequestedDataToBe:nil];
1243 }
1244
1245 TEST(WKAttachmentTests, InvalidateAttachmentsAfterWebProcessTermination)
1246 {
1247     auto webView = webViewForTestingAttachments();
1248     RetainPtr<_WKAttachment> pdfAttachment;
1249     RetainPtr<_WKAttachment> htmlAttachment;
1250     {
1251         ObserveAttachmentUpdatesForScope insertionObserver(webView.get());
1252         pdfAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"attachment.pdf" contentType:@"application/pdf" data:testPDFData()];
1253         htmlAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"index.html" contentType:@"text/html" data:testHTMLData()];
1254         insertionObserver.expectAttachmentUpdates(@[ ], @[ pdfAttachment.get(), htmlAttachment.get() ]);
1255         EXPECT_TRUE([pdfAttachment isConnected]);
1256         EXPECT_TRUE([htmlAttachment isConnected]);
1257     }
1258     {
1259         ObserveAttachmentUpdatesForScope removalObserver(webView.get());
1260         [webView stringByEvaluatingJavaScript:@"getSelection().collapseToEnd()"];
1261         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1262         removalObserver.expectAttachmentUpdates(@[ htmlAttachment.get() ], @[ ]);
1263         EXPECT_TRUE([pdfAttachment isConnected]);
1264         EXPECT_FALSE([htmlAttachment isConnected]);
1265         [htmlAttachment expectRequestedDataToBe:testHTMLData()];
1266     }
1267
1268     __block bool webProcessTerminated = false;
1269     auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
1270     [webView setNavigationDelegate:navigationDelegate.get()];
1271     [navigationDelegate setWebContentProcessDidTerminate:^(WKWebView *) {
1272         webProcessTerminated = true;
1273     }];
1274
1275     ObserveAttachmentUpdatesForScope observer(webView.get());
1276     [webView _killWebContentProcess];
1277     TestWebKitAPI::Util::run(&webProcessTerminated);
1278
1279     observer.expectAttachmentUpdates(@[ pdfAttachment.get() ], @[ ]);
1280     EXPECT_FALSE([pdfAttachment isConnected]);
1281     EXPECT_FALSE([htmlAttachment isConnected]);
1282     [pdfAttachment expectRequestedDataToBe:nil];
1283     [htmlAttachment expectRequestedDataToBe:nil];
1284 }
1285
1286 TEST(WKAttachmentTests, MoveAttachmentElementAsIconByDragging)
1287 {
1288     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1289     [configuration _setAttachmentElementEnabled:YES];
1290     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
1291     TestWKWebView *webView = [simulator webView];
1292     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
1293
1294     auto data = retainPtr(testPDFData());
1295     auto attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"document.pdf" contentType:@"application/pdf" data:data.get()]);
1296
1297     [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
1298     [webView _executeEditCommand:@"InsertHTML" argument:@"<strong>text</strong>" completion:nil];
1299     [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
1300     [webView expectElementTag:@"ATTACHMENT" toComeBefore:@"STRONG"];
1301
1302     // Drag the attachment element to somewhere below the strong text.
1303     [simulator runFrom:[webView attachmentElementMidPoint] to:CGPointMake(50, 300)];
1304
1305     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1306     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1307     [attachment expectRequestedDataToBe:data.get()];
1308     EXPECT_EQ([simulator insertedAttachments].count, [simulator removedAttachments].count);
1309 #if PLATFORM(MAC)
1310     EXPECT_FALSE(isCompletelyTransparent([simulator draggingInfo].draggedImage));
1311 #endif
1312
1313     [webView expectElementTag:@"STRONG" toComeBefore:@"ATTACHMENT"];
1314     [simulator endDataTransfer];
1315 }
1316
1317 TEST(WKAttachmentTests, PasteWebArchiveContainingImages)
1318 {
1319     NSData *markupData = [@"<img src='1.png' alt='foo'><div><br></div><img src='2.gif' alt='bar'>" dataUsingEncoding:NSUTF8StringEncoding];
1320
1321     auto mainResource = adoptNS([[WebResource alloc] initWithData:markupData URL:[NSURL URLWithString:@"foo.html"] MIMEType:@"text/html" textEncodingName:@"utf-8" frameName:nil]);
1322     auto pngResource = adoptNS([[WebResource alloc] initWithData:testImageData() URL:[NSURL URLWithString:@"1.png"] MIMEType:@"image/png" textEncodingName:nil frameName:nil]);
1323     auto gifResource = adoptNS([[WebResource alloc] initWithData:testGIFData() URL:[NSURL URLWithString:@"2.gif"] MIMEType:@"image/gif" textEncodingName:nil frameName:nil]);
1324     auto archive = adoptNS([[WebArchive alloc] initWithMainResource:mainResource.get() subresources:@[ pngResource.get(), gifResource.get() ] subframeArchives:@[ ]]);
1325
1326 #if PLATFORM(MAC)
1327     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1328     [pasteboard declareTypes:@[WebArchivePboardType] owner:nil];
1329     [pasteboard setData:[archive data] forType:WebArchivePboardType];
1330 #else
1331     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1332     [pasteboard setData:[archive data] forPasteboardType:WebArchivePboardType];
1333 #endif
1334
1335     RetainPtr<_WKAttachment> gifAttachment;
1336     RetainPtr<_WKAttachment> pngAttachment;
1337     auto webView = webViewForTestingAttachments();
1338
1339     ObserveAttachmentUpdatesForScope observer(webView.get());
1340     [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1341     [webView expectElementCount:2 tagName:@"IMG"];
1342
1343     for (_WKAttachment *attachment in observer.observer().inserted) {
1344         if ([attachment.info.contentType isEqualToString:@"image/png"])
1345             pngAttachment = attachment;
1346         else if ([attachment.info.contentType isEqualToString:@"image/gif"])
1347             gifAttachment = attachment;
1348     }
1349
1350     EXPECT_WK_STREQ("foo", [pngAttachment info].name);
1351     EXPECT_WK_STREQ("bar", [gifAttachment info].name);
1352     [pngAttachment expectRequestedDataToBe:testImageData()];
1353     [gifAttachment expectRequestedDataToBe:testGIFData()];
1354     observer.expectAttachmentUpdates(@[ ], @[ pngAttachment.get(), gifAttachment.get() ]);
1355 }
1356
1357 TEST(WKAttachmentTests, ChangeFileWrapperForPastedImage)
1358 {
1359     platformCopyPNG();
1360     auto webView = webViewForTestingAttachments();
1361
1362     ObserveAttachmentUpdatesForScope observer(webView.get());
1363     [webView paste:nil];
1364     [webView waitForImageElementSizeToBecome:CGSizeMake(215, 174)];
1365
1366     auto attachment = retainPtr(observer.observer().inserted.firstObject);
1367     auto originalImageData = retainPtr([attachment info].fileWrapper);
1368     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"HTMLAttachmentElement.getAttachmentIdentifier(document.querySelector('img'))"]);
1369
1370     auto newImage = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testGIFData()]);
1371     [newImage setPreferredFilename:@"foo.gif"];
1372     [attachment synchronouslySetFileWrapper:newImage.get() newContentType:nil error:nil];
1373     [webView waitForImageElementSizeToBecome:CGSizeMake(52, 64)];
1374
1375     [attachment synchronouslySetFileWrapper:originalImageData.get() newContentType:@"image/png" error:nil];
1376     [webView waitForImageElementSizeToBecome:CGSizeMake(215, 174)];
1377 }
1378
1379 TEST(WKAttachmentTests, AddAttachmentToConnectedImageElement)
1380 {
1381     auto webView = webViewForTestingAttachments();
1382     [webView _synchronouslyExecuteEditCommand:@"InsertHTML" argument:@"<img></img>"];
1383
1384     ObserveAttachmentUpdatesForScope observer(webView.get());
1385     NSString *attachmentIdentifier = [webView stringByEvaluatingJavaScript:@"HTMLAttachmentElement.getAttachmentIdentifier(document.querySelector('img'))"];
1386     auto attachment = retainPtr([webView _attachmentForIdentifier:attachmentIdentifier]);
1387     EXPECT_WK_STREQ(attachmentIdentifier, [attachment uniqueIdentifier]);
1388     EXPECT_WK_STREQ(attachmentIdentifier, [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"]);
1389     observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
1390
1391     auto firstImage = adoptNS([[NSFileWrapper alloc] initWithURL:testImageFileURL() options:0 error:nil]);
1392     auto secondImage = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testGIFData()]);
1393     [secondImage setPreferredFilename:@"foo.gif"];
1394
1395     [attachment synchronouslySetFileWrapper:firstImage.get() newContentType:@"image/png" error:nil];
1396     [webView waitForImageElementSizeToBecome:CGSizeMake(215, 174)];
1397
1398     [attachment synchronouslySetFileWrapper:secondImage.get() newContentType:(__bridge NSString *)kUTTypeGIF error:nil];
1399     [webView waitForImageElementSizeToBecome:CGSizeMake(52, 64)];
1400
1401     [attachment synchronouslySetFileWrapper:firstImage.get() newContentType:nil error:nil];
1402     [webView waitForImageElementSizeToBecome:CGSizeMake(215, 174)];
1403 }
1404
1405 TEST(WKAttachmentTests, ConnectImageWithAttachmentToDocument)
1406 {
1407     auto webView = webViewForTestingAttachments();
1408     ObserveAttachmentUpdatesForScope observer(webView.get());
1409
1410     NSString *identifier = [webView stringByEvaluatingJavaScript:@"image = document.createElement('img'); HTMLAttachmentElement.getAttachmentIdentifier(image)"];
1411     auto image = adoptNS([[NSFileWrapper alloc] initWithURL:testImageFileURL() options:0 error:nil]);
1412     auto attachment = retainPtr([webView _attachmentForIdentifier:identifier]);
1413     [attachment synchronouslySetFileWrapper:image.get() newContentType:nil error:nil];
1414     EXPECT_FALSE([attachment isConnected]);
1415     observer.expectAttachmentUpdates(@[ ], @[ ]);
1416
1417     [webView evaluateJavaScript:@"document.body.appendChild(image)" completionHandler:nil];
1418     [webView waitForImageElementSizeToBecome:CGSizeMake(215, 174)];
1419     EXPECT_TRUE([attachment isConnected]);
1420     observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
1421
1422     auto newImage = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testGIFData()]);
1423     [newImage setPreferredFilename:@"test.gif"];
1424     [attachment synchronouslySetFileWrapper:newImage.get() newContentType:nil error:nil];
1425     [webView waitForImageElementSizeToBecome:CGSizeMake(52, 64)];
1426 }
1427
1428 TEST(WKAttachmentTests, CustomFileWrapperSubclass)
1429 {
1430     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1431     [configuration _setAttachmentElementEnabled:YES];
1432     RetainPtr<NSException> exception;
1433     @try {
1434         [configuration _setAttachmentFileWrapperClass:[NSArray self]];
1435     } @catch(NSException *caught) {
1436         exception = caught;
1437     }
1438     EXPECT_TRUE(exception);
1439
1440     [configuration _setAttachmentFileWrapperClass:[FileWrapper self]];
1441
1442     auto webView = webViewForTestingAttachments(CGSizeZero, configuration.get());
1443
1444     ObserveAttachmentUpdatesForScope observer(webView.get());
1445     platformCopyPNG();
1446     [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1447     NSArray<_WKAttachment *> * insertedAttachments = observer.observer().inserted;
1448
1449     EXPECT_EQ(1U, insertedAttachments.count);
1450     EXPECT_EQ([FileWrapper self], [insertedAttachments.firstObject.info.fileWrapper class]);
1451 }
1452
1453 TEST(WKAttachmentTests, CopyAndPasteBetweenWebViews)
1454 {
1455     auto file = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testHTMLData()]);
1456     [file setPreferredFilename:@"test.foobar"];
1457     auto image = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testImageData()]);
1458     auto document = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testPDFData()]);
1459     auto folder = adoptNS([[NSFileWrapper alloc] initDirectoryWithFileWrappers:@{ @"image.png": image.get(), @"document.pdf": document.get() }]);
1460     [folder setPreferredFilename:@"folder"];
1461     auto archive = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:testZIPData()]);
1462     [archive setPreferredFilename:@"archive"];
1463
1464     @autoreleasepool {
1465         auto firstWebView = webViewForTestingAttachments();
1466         [firstWebView synchronouslyInsertAttachmentWithFileWrapper:file.get() contentType:@"application/octet-stream"];
1467         [firstWebView synchronouslyInsertAttachmentWithFileWrapper:folder.get() contentType:(__bridge NSString *)kUTTypeFolder];
1468         [firstWebView synchronouslyInsertAttachmentWithFileWrapper:archive.get() contentType:@"application/zip"];
1469         [firstWebView selectAll:nil];
1470         [firstWebView _executeEditCommand:@"Copy" argument:nil completion:nil];
1471     }
1472
1473     auto secondWebView = webViewForTestingAttachments();
1474     ObserveAttachmentUpdatesForScope observer(secondWebView.get());
1475     [secondWebView paste:nil];
1476     [secondWebView expectElementCount:3 tagName:@"attachment"];
1477     EXPECT_EQ(3U, observer.observer().inserted.count);
1478
1479     NSString *plainFileIdentifier = [secondWebView stringByEvaluatingJavaScript:@"document.querySelector('attachment[title^=test]').uniqueIdentifier"];
1480     NSString *folderIdentifier = [secondWebView stringByEvaluatingJavaScript:@"document.querySelector('attachment[title=folder]').uniqueIdentifier"];
1481     NSString *archiveIdentifier = [secondWebView stringByEvaluatingJavaScript:@"document.querySelector('attachment[title=archive]').uniqueIdentifier"];
1482
1483     _WKAttachmentInfo *pastedFileInfo = [secondWebView _attachmentForIdentifier:plainFileIdentifier].info;
1484     _WKAttachmentInfo *pastedFolderInfo = [secondWebView _attachmentForIdentifier:folderIdentifier].info;
1485     _WKAttachmentInfo *pastedArchiveInfo = [secondWebView _attachmentForIdentifier:archiveIdentifier].info;
1486
1487     NSDictionary<NSString *, NSFileWrapper *> *pastedFolderContents = pastedFolderInfo.fileWrapper.fileWrappers;
1488     NSFileWrapper *documentFromFolder = [pastedFolderContents objectForKey:@"document.pdf"];
1489     NSFileWrapper *imageFromFolder = [pastedFolderContents objectForKey:@"image.png"];
1490     EXPECT_TRUE([[document regularFileContents] isEqualToData:documentFromFolder.regularFileContents]);
1491     EXPECT_TRUE([[image regularFileContents] isEqualToData:imageFromFolder.regularFileContents]);
1492     EXPECT_TRUE([[file regularFileContents] isEqualToData:pastedFileInfo.fileWrapper.regularFileContents]);
1493     EXPECT_TRUE([[archive regularFileContents] isEqualToData:pastedArchiveInfo.fileWrapper.regularFileContents]);
1494     EXPECT_WK_STREQ("application/octet-stream", pastedFileInfo.contentType);
1495     EXPECT_WK_STREQ("public.directory", pastedFolderInfo.contentType);
1496     EXPECT_WK_STREQ("application/zip", pastedArchiveInfo.contentType);
1497 }
1498
1499 #pragma mark - Platform-specific tests
1500
1501 #if PLATFORM(MAC)
1502
1503 TEST(WKAttachmentTestsMac, InsertPastedFileURLsAsAttachments)
1504 {
1505     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1506     [pasteboard clearContents];
1507     [pasteboard declareTypes:@[NSFilenamesPboardType] owner:nil];
1508     [pasteboard setPropertyList:@[testPDFFileURL().path, testImageFileURL().path] forType:NSFilenamesPboardType];
1509
1510     RetainPtr<NSArray<_WKAttachment *>> insertedAttachments;
1511     auto webView = webViewForTestingAttachments();
1512     {
1513         ObserveAttachmentUpdatesForScope observer(webView.get());
1514         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1515         insertedAttachments = [observer.observer() inserted];
1516         EXPECT_EQ(2U, [insertedAttachments count]);
1517     }
1518
1519     [webView expectElementCount:1 tagName:@"ATTACHMENT"];
1520     [webView expectElementCount:1 tagName:@"IMG"];
1521     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').getAttribute('type')"]);
1522     EXPECT_WK_STREQ("test.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').getAttribute('title')"]);
1523
1524     NSString *imageAttachmentIdentifier = [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"];
1525     if ([testImageData() isEqualToData:[insertedAttachments firstObject].info.data])
1526         EXPECT_WK_STREQ([insertedAttachments firstObject].uniqueIdentifier, imageAttachmentIdentifier);
1527     else
1528         EXPECT_WK_STREQ([insertedAttachments lastObject].uniqueIdentifier, imageAttachmentIdentifier);
1529
1530     for (_WKAttachment *attachment in insertedAttachments.get())
1531         EXPECT_GT(attachment.info.filePath.length, 0U);
1532
1533     {
1534         ObserveAttachmentUpdatesForScope observer(webView.get());
1535         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1536         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1537         NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
1538         EXPECT_EQ(2U, removedAttachments.count);
1539         EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments firstObject]]);
1540         EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments lastObject]]);
1541     }
1542 }
1543
1544 TEST(WKAttachmentTestsMac, InsertDroppedFilePromisesAsAttachments)
1545 {
1546     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1547     [configuration _setAttachmentElementEnabled:YES];
1548     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
1549     TestWKWebView *webView = [simulator webView];
1550     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
1551     [simulator writePromisedFiles:@[ testPDFFileURL(), testImageFileURL() ]];
1552
1553     [simulator runFrom:CGPointMake(0, 0) to:CGPointMake(50, 50)];
1554
1555     [webView expectElementCount:1 tagName:@"ATTACHMENT"];
1556     [webView expectElementCount:1 tagName:@"IMG"];
1557     EXPECT_EQ(2U, [simulator insertedAttachments].count);
1558
1559     auto insertedAttachments = retainPtr([simulator insertedAttachments]);
1560     NSArray<NSData *> *expectedData = @[ testPDFData(), testImageData() ];
1561     for (_WKAttachment *attachment in insertedAttachments.get()) {
1562         EXPECT_GT(attachment.info.filePath.length, 0U);
1563         EXPECT_TRUE([expectedData containsObject:attachment.info.data]);
1564         if ([testPDFData() isEqualToData:attachment.info.data])
1565             EXPECT_WK_STREQ("application/pdf", attachment.info.contentType);
1566         else if ([testImageData() isEqualToData:attachment.info.data]) {
1567             EXPECT_WK_STREQ("image/png", attachment.info.contentType);
1568             EXPECT_WK_STREQ(attachment.uniqueIdentifier, [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"]);
1569         }
1570     }
1571
1572     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1573     [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1574     auto removedAttachments = retainPtr([simulator removedAttachments]);
1575     EXPECT_EQ(2U, [removedAttachments count]);
1576     [webView expectElementCount:0 tagName:@"ATTACHMENT"];
1577     [webView expectElementCount:0 tagName:@"IMG"];
1578     EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments firstObject]]);
1579     EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments lastObject]]);
1580 }
1581
1582 TEST(WKAttachmentTestsMac, DragAttachmentAsFilePromise)
1583 {
1584     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
1585     [configuration _setAttachmentElementEnabled:YES];
1586     auto simulator = adoptNS([[DragAndDropSimulator alloc] initWithWebViewFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
1587     TestWKWebView *webView = [simulator webView];
1588     [webView synchronouslyLoadHTMLString:attachmentEditingTestMarkup];
1589
1590     auto fileWrapper = adoptNS([[NSFileWrapper alloc] initWithURL:testPDFFileURL() options:0 error:nil]);
1591     auto attachment = retainPtr([webView synchronouslyInsertAttachmentWithFileWrapper:fileWrapper.get() contentType:nil]);
1592     [simulator runFrom:[webView attachmentElementMidPoint] to:CGPointMake(300, 300)];
1593
1594     NSArray<NSURL *> *urls = [simulator receivePromisedFiles];
1595     EXPECT_EQ(1U, urls.count);
1596     EXPECT_WK_STREQ("test.pdf", urls.lastObject.lastPathComponent);
1597     EXPECT_TRUE([[NSData dataWithContentsOfURL:urls.firstObject] isEqualToData:testPDFData()]);
1598     EXPECT_FALSE(isCompletelyTransparent([simulator draggingInfo].draggedImage));
1599 }
1600
1601 #endif // PLATFORM(MAC)
1602
1603 #if PLATFORM(IOS_FAMILY)
1604
1605 TEST(WKAttachmentTestsIOS, InsertDroppedImageAsAttachment)
1606 {
1607     auto webView = webViewForTestingAttachments();
1608     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1609     auto item = adoptNS([[NSItemProvider alloc] init]);
1610     [item registerData:testImageData() type:(__bridge NSString *)kUTTypePNG];
1611     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1612     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1613
1614     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1615     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1616     auto attachment = retainPtr([dragAndDropSimulator insertedAttachments].firstObject);
1617     [attachment expectRequestedDataToBe:testImageData()];
1618     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"]);
1619
1620     {
1621         ObserveAttachmentUpdatesForScope observer(webView.get());
1622         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1623         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1624         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
1625     }
1626 }
1627
1628 TEST(WKAttachmentTestsIOS, InsertDroppedAttributedStringContainingAttachment)
1629 {
1630     auto webView = webViewForTestingAttachments();
1631     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1632     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(__bridge NSString *)kUTTypePNG]);
1633     auto item = adoptNS([[NSItemProvider alloc] init]);
1634     [item registerObject:[NSAttributedString attributedStringWithAttachment:image.get()] visibility:NSItemProviderRepresentationVisibilityAll];
1635
1636     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1637     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1638
1639     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1640     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1641     auto attachment = retainPtr([dragAndDropSimulator insertedAttachments].firstObject);
1642
1643     auto size = platformImageWithData([attachment info].data).size;
1644     EXPECT_EQ(215., size.width);
1645     EXPECT_EQ(174., size.height);
1646     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('img').attachmentIdentifier"]);
1647
1648     {
1649         ObserveAttachmentUpdatesForScope observer(webView.get());
1650         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1651         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1652         observer.expectAttachmentUpdates(@[ attachment.get() ], @[ ]);
1653     }
1654 }
1655
1656 TEST(WKAttachmentTestsIOS, InsertDroppedRichAndPlainTextFilesAsAttachments)
1657 {
1658     // Here, both rich text and plain text are content types that WebKit already understands how to insert in editable
1659     // areas in the absence of attachment elements. However, due to the explicitly set attachment presentation style
1660     // on the item providers, we should instead treat them as dropped files and insert attachment elements.
1661     // This exercises the scenario of dragging rich and plain text files from Files to Mail.
1662     auto richTextItem = adoptNS([[NSItemProvider alloc] init]);
1663     auto richText = adoptNS([[NSAttributedString alloc] initWithString:@"Hello world" attributes:@{ NSFontAttributeName: [UIFont boldSystemFontOfSize:12] }]);
1664     [richTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1665     [richTextItem registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
1666     [richTextItem setSuggestedName:@"hello.rtf"];
1667
1668     auto plainTextItem = adoptNS([[NSItemProvider alloc] init]);
1669     [plainTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1670     [plainTextItem registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll];
1671     [plainTextItem setSuggestedName:@"world.txt"];
1672
1673     auto webView = webViewForTestingAttachments();
1674     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1675     [dragAndDropSimulator setExternalItemProviders:@[ richTextItem.get(), plainTextItem.get() ]];
1676     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1677
1678     EXPECT_EQ(2U, [dragAndDropSimulator insertedAttachments].count);
1679     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1680
1681     for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments])
1682         EXPECT_GT([attachment info].data.length, 0U);
1683
1684     [webView expectElementCount:2 tagName:@"ATTACHMENT"];
1685     EXPECT_WK_STREQ("hello.rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
1686     EXPECT_WK_STREQ("text/rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1687     EXPECT_WK_STREQ("world.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
1688     EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
1689 }
1690
1691 TEST(WKAttachmentTestsIOS, InsertDroppedZipArchiveAsAttachment)
1692 {
1693     // Since WebKit doesn't have any default DOM representation for ZIP archives, we should fall back to inserting
1694     // attachment elements. This exercises the flow of dragging a ZIP file from an app that doesn't specify a preferred
1695     // presentation style (e.g. Notes) into Mail.
1696     auto item = adoptNS([[NSItemProvider alloc] init]);
1697     NSData *data = testZIPData();
1698     [item registerData:data type:(__bridge NSString *)kUTTypeZipArchive];
1699     [item setSuggestedName:@"archive.zip"];
1700
1701     auto webView = webViewForTestingAttachments();
1702     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1703     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1704     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1705
1706     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1707     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1708     [[dragAndDropSimulator insertedAttachments].firstObject expectRequestedDataToBe:data];
1709     [webView expectElementCount:1 tagName:@"ATTACHMENT"];
1710     EXPECT_WK_STREQ("archive.zip", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1711     EXPECT_WK_STREQ("application/zip", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1712 }
1713
1714 TEST(WKAttachmentTestsIOS, InsertDroppedItemProvidersInOrder)
1715 {
1716     // Tests that item providers are inserted in the order they are specified. In this case, the two inserted attachments
1717     // should be separated by a link.
1718     auto firstAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
1719     [firstAttachmentItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1720     [firstAttachmentItem registerObject:@"FIRST" visibility:NSItemProviderRepresentationVisibilityAll];
1721     [firstAttachmentItem setSuggestedName:@"first.txt"];
1722
1723     auto inlineTextItem = adoptNS([[NSItemProvider alloc] init]);
1724     auto appleURL = retainPtr([NSURL URLWithString:@"https://www.apple.com/"]);
1725     [inlineTextItem registerObject:appleURL.get() visibility:NSItemProviderRepresentationVisibilityAll];
1726
1727     auto secondAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
1728     [secondAttachmentItem registerData:testPDFData() type:(__bridge NSString *)kUTTypePDF];
1729     [secondAttachmentItem setSuggestedName:@"second.pdf"];
1730
1731     auto webView = webViewForTestingAttachments();
1732     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1733     [dragAndDropSimulator setExternalItemProviders:@[ firstAttachmentItem.get(), inlineTextItem.get(), secondAttachmentItem.get() ]];
1734     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1735
1736     EXPECT_EQ(2U, [dragAndDropSimulator insertedAttachments].count);
1737     EXPECT_EQ(0U, [dragAndDropSimulator removedAttachments].count);
1738
1739     for (_WKAttachment *attachment in [dragAndDropSimulator insertedAttachments])
1740         EXPECT_GT([attachment info].data.length, 0U);
1741
1742     [webView expectElementTagsInOrder:@[ @"ATTACHMENT", @"A", @"ATTACHMENT" ]];
1743
1744     EXPECT_WK_STREQ("first.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
1745     EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1746     EXPECT_WK_STREQ([appleURL absoluteString], [webView valueOfAttribute:@"href" forQuerySelector:@"a"]);
1747     EXPECT_WK_STREQ("second.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
1748     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
1749 }
1750
1751 TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsFile)
1752 {
1753     auto item = adoptNS([[NSItemProvider alloc] init]);
1754     auto data = retainPtr(testPDFData());
1755     [item registerData:data.get() type:(__bridge NSString *)kUTTypePDF];
1756     [item setSuggestedName:@"document.pdf"];
1757
1758     auto webView = webViewForTestingAttachments();
1759     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1760     [dragAndDropSimulator setExternalItemProviders:@[ item.get() ]];
1761     [dragAndDropSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1762
1763     // First, verify that the attachment was successfully dropped.
1764     EXPECT_EQ(1U, [dragAndDropSimulator insertedAttachments].count);
1765     _WKAttachment *attachment = [dragAndDropSimulator insertedAttachments].firstObject;
1766     [attachment expectRequestedDataToBe:data.get()];
1767     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1768     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1769
1770     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
1771     [dragAndDropSimulator setExternalItemProviders:@[ ]];
1772     [dragAndDropSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
1773
1774     // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
1775     EXPECT_EQ(1U, [dragAndDropSimulator sourceItemProviders].count);
1776     NSItemProvider *itemProvider = [dragAndDropSimulator sourceItemProviders].firstObject;
1777     EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
1778     [itemProvider expectType:(__bridge NSString *)kUTTypePDF withData:data.get()];
1779     EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
1780     [dragAndDropSimulator endDataTransfer];
1781 }
1782
1783 TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsData)
1784 {
1785     auto webView = webViewForTestingAttachments();
1786     auto data = retainPtr(testPDFData());
1787     RetainPtr<_WKAttachment> attachment;
1788     {
1789         ObserveAttachmentUpdatesForScope observer(webView.get());
1790         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"document.pdf" contentType:@"application/pdf" data:data.get()];
1791         observer.expectAttachmentUpdates(@[ ], @[ attachment.get() ]);
1792     }
1793
1794     // First, verify that the attachment was successfully inserted from raw data.
1795     [attachment expectRequestedDataToBe:data.get()];
1796     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1797     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1798
1799     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
1800     auto dragAndDropSimulator = adoptNS([[DragAndDropSimulator alloc] initWithWebView:webView.get()]);
1801     [dragAndDropSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
1802
1803     // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
1804     EXPECT_EQ(1U, [dragAndDropSimulator sourceItemProviders].count);
1805     NSItemProvider *itemProvider = [dragAndDropSimulator sourceItemProviders].firstObject;
1806     EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
1807     [itemProvider expectType:(__bridge NSString *)kUTTypePDF withData:data.get()];
1808     EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
1809     [dragAndDropSimulator endDataTransfer];
1810 }
1811
1812 #endif // PLATFORM(IOS_FAMILY)
1813
1814 } // namespace TestWebKitAPI
1815
1816 #endif // WK_API_ENABLED && !PLATFORM(WATCHOS) && !PLATFORM(TVOS)