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