ASSERTION FAILED: registration || isTerminating() in WebCore::SWServerWorker::skipWai...
[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 "DataInteractionSimulator.h"
29 #import "PlatformUtilities.h"
30 #import "TestWKWebView.h"
31 #import <WebKit/WKPreferencesRefPrivate.h>
32 #import <WebKit/WKWebViewPrivate.h>
33 #import <WebKit/WebKit.h>
34 #import <WebKit/WebKitPrivate.h>
35 #import <wtf/RetainPtr.h>
36
37 #if PLATFORM(IOS)
38 #import <MobileCoreServices/MobileCoreServices.h>
39 #endif
40
41 #define USES_MODERN_ATTRIBUTED_STRING_CONVERSION ((PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300))
42
43 #if WK_API_ENABLED && !PLATFORM(WATCHOS) && !PLATFORM(TVOS)
44
45 @interface AttachmentUpdateObserver : NSObject <WKUIDelegatePrivate>
46 @property (nonatomic, readonly) NSArray *inserted;
47 @property (nonatomic, readonly) NSArray *removed;
48 @end
49
50 @implementation AttachmentUpdateObserver {
51     RetainPtr<NSMutableArray<_WKAttachment *>> _inserted;
52     RetainPtr<NSMutableArray<_WKAttachment *>> _removed;
53 }
54
55 - (instancetype)init
56 {
57     if (self = [super init]) {
58         _inserted = adoptNS([[NSMutableArray alloc] init]);
59         _removed = adoptNS([[NSMutableArray alloc] init]);
60     }
61     return self;
62 }
63
64 - (NSArray<_WKAttachment *> *)inserted
65 {
66     return _inserted.get();
67 }
68
69 - (NSArray<_WKAttachment *> *)removed
70 {
71     return _removed.get();
72 }
73
74 - (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment
75 {
76     [_inserted addObject:attachment];
77 }
78
79 - (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment
80 {
81     [_removed addObject:attachment];
82 }
83
84 @end
85
86 namespace TestWebKitAPI {
87
88 class ObserveAttachmentUpdatesForScope {
89 public:
90     ObserveAttachmentUpdatesForScope(TestWKWebView *webView)
91         : m_webView(webView)
92     {
93         m_previousDelegate = webView.UIDelegate;
94         m_observer = adoptNS([[AttachmentUpdateObserver alloc] init]);
95         webView.UIDelegate = m_observer.get();
96     }
97
98     ~ObserveAttachmentUpdatesForScope()
99     {
100         [m_webView setUIDelegate:m_previousDelegate.get()];
101     }
102
103     AttachmentUpdateObserver *observer() const { return m_observer.get(); }
104
105     void expectAttachmentUpdates(NSArray<_WKAttachment *> *removed, NSArray<_WKAttachment *> *inserted)
106     {
107         BOOL removedAttachmentsMatch = [observer().removed isEqual:removed];
108         if (!removedAttachmentsMatch)
109             NSLog(@"Expected removed attachments: %@ to match %@.", observer().removed, removed);
110         EXPECT_TRUE(removedAttachmentsMatch);
111
112         BOOL insertedAttachmentsMatch = [observer().inserted isEqual:inserted];
113         if (!insertedAttachmentsMatch)
114             NSLog(@"Expected inserted attachments: %@ to match %@.", observer().inserted, inserted);
115         EXPECT_TRUE(insertedAttachmentsMatch);
116     }
117
118 private:
119     RetainPtr<AttachmentUpdateObserver> m_observer;
120     RetainPtr<TestWKWebView> m_webView;
121     RetainPtr<id> m_previousDelegate;
122 };
123
124 }
125
126 @interface TestWKWebView (AttachmentTesting)
127 @end
128
129 static RetainPtr<TestWKWebView> webViewForTestingAttachments()
130 {
131     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
132     [configuration _setAttachmentElementEnabled:YES];
133     WKPreferencesSetCustomPasteboardDataEnabled((WKPreferencesRef)[configuration preferences], YES);
134
135     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()]);
136     [webView synchronouslyLoadHTMLString:@"<script>focus = () => document.body.focus()</script><body onload=focus() contenteditable></body>"];
137
138     return webView;
139 }
140
141 static NSData *testZIPData()
142 {
143     NSURL *zipFileURL = [[NSBundle mainBundle] URLForResource:@"compressed-files" withExtension:@"zip" subdirectory:@"TestWebKitAPI.resources"];
144     return [NSData dataWithContentsOfURL:zipFileURL];
145 }
146
147 static NSData *testHTMLData()
148 {
149     return [@"<a href='#'>This is some HTML data</a>" dataUsingEncoding:NSUTF8StringEncoding];
150 }
151
152 static NSURL *testImageFileURL()
153 {
154     return [[NSBundle mainBundle] URLForResource:@"icon" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"];
155 }
156
157 static NSData *testImageData()
158 {
159     return [NSData dataWithContentsOfURL:testImageFileURL()];
160 }
161
162 static NSData *testVideoData()
163 {
164     NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"mp4" subdirectory:@"TestWebKitAPI.resources"];
165     return [NSData dataWithContentsOfURL:url];
166 }
167
168 static NSURL *testPDFFileURL()
169 {
170     return [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"pdf" subdirectory:@"TestWebKitAPI.resources"];
171 }
172
173 static NSData *testPDFData()
174 {
175     return [NSData dataWithContentsOfURL:testPDFFileURL()];
176 }
177
178 static _WKAttachmentDisplayOptions *displayOptionsWithMode(_WKAttachmentDisplayMode mode)
179 {
180     _WKAttachmentDisplayOptions *options = [[[_WKAttachmentDisplayOptions alloc] init] autorelease];
181     options.mode = mode;
182     return options;
183 }
184
185 @implementation TestWKWebView (AttachmentTesting)
186
187 - (void)expectElementTag:(NSString *)tagName toComeBefore:(NSString *)otherTagName
188 {
189     NSArray *tagsInBody = [self objectByEvaluatingJavaScript:@"Array.from(document.body.getElementsByTagName('*')).map(e => e.tagName)"];
190     BOOL success = [tagsInBody containsObject:tagName] && [tagsInBody containsObject:otherTagName];
191     if (success) {
192         NSUInteger index = [tagsInBody indexOfObject:tagName];
193         NSUInteger otherIndex = [tagsInBody indexOfObjectWithOptions:NSEnumerationReverse passingTest:[&] (NSString *tag, NSUInteger, BOOL *) {
194             return [tag isEqualToString:otherTagName];
195         }];
196         success = index < otherIndex;
197     }
198     EXPECT_TRUE(success);
199     if (!success)
200         NSLog(@"Expected %@ to come before %@ in tags: %@", tagName, otherTagName, tagsInBody);
201 }
202
203 - (BOOL)_synchronouslyExecuteEditCommand:(NSString *)command argument:(NSString *)argument
204 {
205     __block bool done = false;
206     __block bool success;
207     [self _executeEditCommand:command argument:argument completion:^(BOOL completionSuccess) {
208         done = true;
209         success = completionSuccess;
210     }];
211     TestWebKitAPI::Util::run(&done);
212     return success;
213 }
214
215 - (_WKAttachment *)synchronouslyInsertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data options:(_WKAttachmentDisplayOptions *)options
216 {
217     __block bool done = false;
218     RetainPtr<_WKAttachment> attachment = [self _insertAttachmentWithFilename:filename contentType:contentType data:data options:options completion:^(BOOL) {
219         done = true;
220     }];
221     TestWebKitAPI::Util::run(&done);
222     return attachment.autorelease();
223 }
224
225 - (CGSize)attachmentElementSize
226 {
227     __block CGSize size;
228     __block bool doneEvaluatingScript = false;
229     [self evaluateJavaScript:@"r = document.querySelector('attachment').getBoundingClientRect(); [r.width, r.height]" completionHandler:^(NSArray<NSNumber *> *sizeResult, NSError *) {
230         size = CGSizeMake(sizeResult.firstObject.floatValue, sizeResult.lastObject.floatValue);
231         doneEvaluatingScript = true;
232     }];
233     TestWebKitAPI::Util::run(&doneEvaluatingScript);
234     return size;
235 }
236
237 - (void)waitForAttachmentElementSizeToBecome:(CGSize)expectedSize
238 {
239     while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]) {
240         if (CGSizeEqualToSize(self.attachmentElementSize, expectedSize))
241             break;
242     }
243 }
244
245 - (BOOL)hasAttribute:(NSString *)attributeName forQuerySelector:(NSString *)querySelector
246 {
247     return [self stringByEvaluatingJavaScript:[NSString stringWithFormat:@"document.querySelector('%@').hasAttribute('%@')", querySelector, attributeName]].boolValue;
248 }
249
250 - (NSString *)valueOfAttribute:(NSString *)attributeName forQuerySelector:(NSString *)querySelector
251 {
252     return [self stringByEvaluatingJavaScript:[NSString stringWithFormat:@"document.querySelector('%@').getAttribute('%@')", querySelector, attributeName]];
253 }
254
255 - (void)expectUpdatesAfterCommand:(NSString *)command withArgument:(NSString *)argument expectedRemovals:(NSArray<_WKAttachment *> *)removed expectedInsertions:(NSArray<_WKAttachment *> *)inserted
256 {
257     TestWebKitAPI::ObserveAttachmentUpdatesForScope observer(self);
258     EXPECT_TRUE([self _synchronouslyExecuteEditCommand:command argument:argument]);
259     observer.expectAttachmentUpdates(removed, inserted);
260 }
261
262 @end
263
264 @implementation NSData (AttachmentTesting)
265
266 - (NSString *)shortDescription
267 {
268     return [NSString stringWithFormat:@"<%tu bytes>", self.length];
269 }
270
271 @end
272
273 @implementation _WKAttachment (AttachmentTesting)
274
275 - (void)synchronouslySetDisplayOptions:(_WKAttachmentDisplayOptions *)options error:(NSError **)error
276 {
277     __block RetainPtr<NSError> resultError;
278     __block bool done = false;
279     [self setDisplayOptions:options completion:^(NSError *error) {
280         resultError = error;
281         done = true;
282     }];
283
284     TestWebKitAPI::Util::run(&done);
285
286     if (error)
287         *error = resultError.autorelease();
288 }
289
290 - (_WKAttachmentInfo *)synchronouslyRequestInfo:(NSError **)error
291 {
292     __block RetainPtr<_WKAttachmentInfo> result;
293     __block RetainPtr<NSError> resultError;
294     __block bool done = false;
295     [self requestInfo:^(_WKAttachmentInfo *info, NSError *error) {
296         result = info;
297         resultError = error;
298         done = true;
299     }];
300
301     TestWebKitAPI::Util::run(&done);
302
303     if (error)
304         *error = resultError.autorelease();
305
306     return result.autorelease();
307 }
308
309 - (NSData *)synchronouslyRequestData:(NSError **)error
310 {
311     return [self synchronouslyRequestInfo:error].data;
312 }
313
314 - (void)synchronouslySetData:(NSData *)data newContentType:(NSString *)newContentType newFilename:(NSString *)newFilename error:(NSError **)error
315 {
316     __block RetainPtr<NSError> resultError;
317     __block bool done = false;
318     [self setData:data newContentType:newContentType newFilename:newFilename completion:^(NSError *error) {
319         resultError = error;
320         done = true;
321     }];
322
323     TestWebKitAPI::Util::run(&done);
324
325     if (error)
326         *error = resultError.autorelease();
327 }
328
329 - (void)expectRequestedDataToBe:(NSData *)expectedData
330 {
331     NSError *requestError = nil;
332     _WKAttachmentInfo *info = [self synchronouslyRequestInfo:&requestError];
333
334     BOOL observedDataIsEqualToExpectedData = [info.data isEqualToData:expectedData] || info.data == expectedData;
335     EXPECT_TRUE(observedDataIsEqualToExpectedData);
336     if (!observedDataIsEqualToExpectedData) {
337         NSLog(@"Expected data: %@ but observed: %@ for %@", [expectedData shortDescription], [info.data shortDescription], self);
338         NSLog(@"Observed error: %@ while reading data for %@", requestError, self);
339     }
340 }
341
342 @end
343
344 #pragma mark - Platform testing helper functions
345
346 #if PLATFORM(IOS)
347
348 typedef void(^ItemProviderDataLoadHandler)(NSData *, NSError *);
349
350 @implementation NSItemProvider (AttachmentTesting)
351
352 - (void)registerData:(NSData *)data type:(NSString *)type
353 {
354     [self registerDataRepresentationForTypeIdentifier:type visibility:NSItemProviderRepresentationVisibilityAll loadHandler:[protectedData = retainPtr(data)] (ItemProviderDataLoadHandler completionHandler) -> NSProgress * {
355         completionHandler(protectedData.get(), nil);
356         return nil;
357     }];
358 }
359
360 - (void)expectType:(NSString *)type withData:(NSData *)expectedData
361 {
362     BOOL containsType = [self.registeredTypeIdentifiers containsObject:type];
363     EXPECT_TRUE(containsType);
364     if (!containsType) {
365         NSLog(@"Expected: %@ to contain %@", self, type);
366         return;
367     }
368
369     __block bool done = false;
370     [self loadDataRepresentationForTypeIdentifier:type completionHandler:^(NSData *observedData, NSError *error) {
371         EXPECT_TRUE([observedData isEqualToData:expectedData]);
372         if (![observedData isEqualToData:expectedData])
373             NSLog(@"Expected data: <%tu bytes> to be equal to data: <%tu bytes>", observedData.length, expectedData.length);
374         EXPECT_TRUE(!error);
375         if (error)
376             NSLog(@"Encountered error when loading data: %@", error);
377         done = true;
378     }];
379     TestWebKitAPI::Util::run(&done);
380 }
381
382 @end
383
384 #endif // PLATFORM(IOS)
385
386 void platformCopyRichTextWithMultipleAttachments()
387 {
388     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(NSString *)kUTTypePNG]);
389     auto pdf = adoptNS([[NSTextAttachment alloc] initWithData:testPDFData() ofType:(NSString *)kUTTypePDF]);
390     auto zip = adoptNS([[NSTextAttachment alloc] initWithData:testZIPData() ofType:(NSString *)kUTTypeZipArchive]);
391
392     auto richText = adoptNS([[NSMutableAttributedString alloc] init]);
393     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:image.get()]];
394     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:pdf.get()]];
395     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:zip.get()]];
396
397 #if PLATFORM(MAC)
398     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
399     [pasteboard clearContents];
400     [pasteboard writeObjects:@[ richText.get() ]];
401 #elif PLATFORM(IOS)
402     auto item = adoptNS([[NSItemProvider alloc] init]);
403     [item registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
404     [UIPasteboard generalPasteboard].itemProviders = @[ item.get() ];
405 #endif
406 }
407
408 void platformCopyRichTextWithImage()
409 {
410     auto richText = adoptNS([[NSMutableAttributedString alloc] init]);
411     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(NSString *)kUTTypePNG]);
412
413     [richText appendAttributedString:[[[NSAttributedString alloc] initWithString:@"Lorem ipsum "] autorelease]];
414     [richText appendAttributedString:[NSAttributedString attributedStringWithAttachment:image.get()]];
415     [richText appendAttributedString:[[[NSAttributedString alloc] initWithString:@" dolor sit amet."] autorelease]];
416
417 #if PLATFORM(MAC)
418     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
419     [pasteboard clearContents];
420     [pasteboard writeObjects:@[ richText.get() ]];
421 #elif PLATFORM(IOS)
422     auto item = adoptNS([[NSItemProvider alloc] init]);
423     [item registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
424     [UIPasteboard generalPasteboard].itemProviders = @[ item.get() ];
425 #endif
426 }
427
428 void platformCopyPNG()
429 {
430 #if PLATFORM(MAC)
431     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
432     [pasteboard declareTypes:@[NSPasteboardTypePNG] owner:nil];
433     [pasteboard setData:testImageData() forType:NSPasteboardTypePNG];
434 #elif PLATFORM(IOS)
435     UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
436     auto item = adoptNS([[UIItemProvider alloc] init]);
437     [item setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
438     [item registerData:testImageData() type:(NSString *)kUTTypePNG];
439     pasteboard.itemProviders = @[ item.get() ];
440 #endif
441 }
442
443 #if PLATFORM(MAC)
444 typedef NSImage PlatformImage;
445 #else
446 typedef UIImage PlatformImage;
447 #endif
448
449 PlatformImage *platformImageWithData(NSData *data)
450 {
451 #if PLATFORM(MAC)
452     return [[[NSImage alloc] initWithData:data] autorelease];
453 #else
454     return [UIImage imageWithData:data];
455 #endif
456 }
457
458 namespace TestWebKitAPI {
459
460 #pragma mark - Platform-agnostic tests
461
462 TEST(WKAttachmentTests, AttachmentElementInsertion)
463 {
464     auto webView = webViewForTestingAttachments();
465     RetainPtr<_WKAttachment> firstAttachment;
466     RetainPtr<_WKAttachment> secondAttachment;
467     {
468         ObserveAttachmentUpdatesForScope observer(webView.get());
469         // Use the given content type for the attachment element's type.
470         firstAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo" contentType:@"text/html" data:testHTMLData() options:nil];
471         EXPECT_WK_STREQ(@"foo", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
472         EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
473         EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
474         observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
475     }
476
477     _WKAttachmentInfo *info = [firstAttachment synchronouslyRequestInfo:nil];
478     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
479     EXPECT_TRUE([info.contentType isEqualToString:@"text/html"]);
480     EXPECT_TRUE([info.name isEqualToString:@"foo"]);
481     EXPECT_EQ(info.filePath.length, 0U);
482
483     {
484         ObserveAttachmentUpdatesForScope scope(webView.get());
485         // Since no content type is explicitly specified, compute it from the file extension.
486         [webView _executeEditCommand:@"DeleteBackward" argument:nil completion:nil];
487         secondAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"bar.png" contentType:nil data:testImageData() options:nil];
488         EXPECT_WK_STREQ(@"bar.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
489         EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
490         EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
491         scope.expectAttachmentUpdates(@[ firstAttachment.get() ], @[ secondAttachment.get() ]);
492     }
493
494     [firstAttachment expectRequestedDataToBe:nil];
495     [secondAttachment expectRequestedDataToBe:testImageData()];
496     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
497     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
498     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
499 }
500
501 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingAndDeletingNewline)
502 {
503     auto webView = webViewForTestingAttachments();
504     RetainPtr<_WKAttachment> attachment;
505     {
506         ObserveAttachmentUpdatesForScope observer(webView.get());
507         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil];
508         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
509     }
510     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
511     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
512     [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body)"];
513     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
514
515     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
516
517     _WKAttachmentInfo *info = [attachment synchronouslyRequestInfo:nil];
518     EXPECT_TRUE([info.data isEqualToData:testHTMLData()]);
519     EXPECT_TRUE([info.contentType isEqualToString:@"text/plain"]);
520     EXPECT_TRUE([info.name isEqualToString:@"foo.txt"]);
521     EXPECT_EQ(info.filePath.length, 0U);
522
523     [webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
524     [attachment expectRequestedDataToBe:nil];
525 }
526
527 TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing)
528 {
529     auto webView = webViewForTestingAttachments();
530     RetainPtr<NSData> htmlData = testHTMLData();
531     RetainPtr<_WKAttachment> attachment;
532     {
533         ObserveAttachmentUpdatesForScope observer(webView.get());
534         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil];
535         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
536     }
537     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
538     [attachment expectRequestedDataToBe:nil];
539
540     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
541     [attachment expectRequestedDataToBe:htmlData.get()];
542
543     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
544     [attachment expectRequestedDataToBe:nil];
545
546     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
547     [attachment expectRequestedDataToBe:htmlData.get()];
548
549     [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
550     [attachment expectRequestedDataToBe:nil];
551 }
552
553 TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles)
554 {
555     auto webView = webViewForTestingAttachments();
556     RetainPtr<_WKAttachment> attachment;
557     [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello"];
558     {
559         ObserveAttachmentUpdatesForScope observer(webView.get());
560         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil];
561         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
562     }
563     [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"World" expectedRemovals:@[] expectedInsertions:@[]];
564     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
565     [webView expectUpdatesAfterCommand:@"ToggleBold" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
566     [webView expectUpdatesAfterCommand:@"ToggleItalic" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
567     [webView expectUpdatesAfterCommand:@"ToggleUnderline" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
568     [attachment expectRequestedDataToBe:testHTMLData()];
569     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
570     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
571     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
572
573     // Inserting text should delete the current selection, removing the attachment in the process.
574     [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"foo" expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
575     [attachment expectRequestedDataToBe:nil];
576 }
577
578 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingLists)
579 {
580     auto webView = webViewForTestingAttachments();
581     RetainPtr<_WKAttachment> attachment;
582     {
583         ObserveAttachmentUpdatesForScope observer(webView.get());
584         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil];
585         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
586     }
587     [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
588     // This edit command behaves more like a "toggle", and will actually break us out of the list we just inserted.
589     [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
590     [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
591     [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
592     [attachment expectRequestedDataToBe:testHTMLData()];
593     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
594     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
595     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
596 }
597
598 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingRichMarkup)
599 {
600     auto webView = webViewForTestingAttachments();
601     RetainPtr<_WKAttachment> attachment;
602     {
603         ObserveAttachmentUpdatesForScope observer(webView.get());
604         [webView _synchronouslyExecuteEditCommand:@"InsertHTML" argument:@"<div><strong><attachment title='a'></attachment></strong></div>"];
605         attachment = observer.observer().inserted[0];
606         observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
607     }
608     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
609     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
610     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
611     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
612     {
613         ObserveAttachmentUpdatesForScope observer(webView.get());
614         [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').remove()"];
615         [webView waitForNextPresentationUpdate];
616         observer.expectAttachmentUpdates(@[attachment.get()], @[ ]);
617     }
618     [attachment expectRequestedDataToBe:nil];
619 }
620
621 TEST(WKAttachmentTests, AttachmentUpdatesWhenCuttingAndPasting)
622 {
623     auto webView = webViewForTestingAttachments();
624     RetainPtr<_WKAttachment> attachment;
625     {
626         ObserveAttachmentUpdatesForScope observer(webView.get());
627         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil];
628         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
629     }
630     [attachment expectRequestedDataToBe:testHTMLData()];
631     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
632     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
633     {
634         ObserveAttachmentUpdatesForScope observer(webView.get());
635         [webView _synchronouslyExecuteEditCommand:@"Cut" argument:nil];
636         observer.expectAttachmentUpdates(@[attachment.get()], @[]);
637     }
638     [attachment expectRequestedDataToBe:nil];
639     {
640         ObserveAttachmentUpdatesForScope observer(webView.get());
641         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
642         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
643     }
644     [attachment expectRequestedDataToBe:testHTMLData()];
645     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentbloburl" forQuerySelector:@"attachment"]);
646     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentpath" forQuerySelector:@"attachment"]);
647     EXPECT_FALSE([webView hasAttribute:@"webkitattachmentid" forQuerySelector:@"attachment"]);
648 }
649
650 TEST(WKAttachmentTests, AttachmentDataForEmptyFile)
651 {
652     auto webView = webViewForTestingAttachments();
653     RetainPtr<_WKAttachment> attachment;
654     {
655         ObserveAttachmentUpdatesForScope observer(webView.get());
656         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"empty.txt" contentType:@"text/plain" data:[NSData data] options:nil];
657         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
658     }
659     [attachment expectRequestedDataToBe:[NSData data]];
660     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
661     {
662         ObserveAttachmentUpdatesForScope scope(webView.get());
663         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
664         scope.expectAttachmentUpdates(@[attachment.get()], @[]);
665     }
666     [attachment expectRequestedDataToBe:nil];
667 }
668
669 TEST(WKAttachmentTests, MultipleSimultaneousAttachmentDataRequests)
670 {
671     auto webView = webViewForTestingAttachments();
672     RetainPtr<NSData> htmlData = testHTMLData();
673     RetainPtr<_WKAttachment> attachment;
674     {
675         ObserveAttachmentUpdatesForScope observer(webView.get());
676         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:htmlData.get() options:nil];
677         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
678     }
679     __block RetainPtr<NSData> dataForFirstRequest;
680     __block RetainPtr<NSData> dataForSecondRequest;
681     __block bool done = false;
682     [attachment requestInfo:^(_WKAttachmentInfo *info, NSError *error) {
683         EXPECT_TRUE(!error);
684         dataForFirstRequest = info.data;
685     }];
686     [attachment requestInfo:^(_WKAttachmentInfo *info, NSError *error) {
687         EXPECT_TRUE(!error);
688         dataForSecondRequest = info.data;
689         done = true;
690     }];
691
692     Util::run(&done);
693
694     EXPECT_TRUE([dataForFirstRequest isEqualToData:htmlData.get()]);
695     EXPECT_TRUE([dataForSecondRequest isEqualToData:htmlData.get()]);
696 }
697
698 TEST(WKAttachmentTests, InPlaceImageAttachmentToggleDisplayMode)
699 {
700     auto webView = webViewForTestingAttachments();
701     RetainPtr<NSData> imageData = testImageData();
702     RetainPtr<_WKAttachment> attachment;
703     {
704         ObserveAttachmentUpdatesForScope observer(webView.get());
705         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"icon.png" contentType:@"image/png" data:imageData.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeAsIcon)];
706         [attachment expectRequestedDataToBe:imageData.get()];
707         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
708     }
709     CGSize iconAttachmentSize = [webView attachmentElementSize];
710
711     [attachment synchronouslySetDisplayOptions:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace) error:nil];
712     [attachment expectRequestedDataToBe:imageData.get()];
713     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(215, 174)];
714
715     [attachment synchronouslySetDisplayOptions:displayOptionsWithMode(_WKAttachmentDisplayModeAsIcon) error:nil];
716     [attachment expectRequestedDataToBe:imageData.get()];
717     [webView waitForAttachmentElementSizeToBecome:iconAttachmentSize];
718
719     [attachment synchronouslySetDisplayOptions:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace) error:nil];
720     [attachment expectRequestedDataToBe:imageData.get()];
721     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(215, 174)];
722 }
723
724 TEST(WKAttachmentTests, InPlaceImageAttachmentParagraphInsertion)
725 {
726     auto webView = webViewForTestingAttachments();
727     RetainPtr<NSData> imageData = testImageData();
728     RetainPtr<_WKAttachment> attachment;
729     {
730         ObserveAttachmentUpdatesForScope observer(webView.get());
731         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"icon.png" contentType:@"image/png" data:imageData.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace)];
732         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
733     }
734     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
735     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
736     [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body)"];
737     [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
738     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
739
740     [attachment expectRequestedDataToBe:imageData.get()];
741     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
742     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(215, 174)];
743
744     [webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
745 }
746
747 TEST(WKAttachmentTests, InPlaceVideoAttachmentInsertionWithinList)
748 {
749     auto webView = webViewForTestingAttachments();
750     RetainPtr<NSData> videoData = testVideoData();
751     RetainPtr<_WKAttachment> attachment;
752
753     [webView _synchronouslyExecuteEditCommand:@"InsertOrderedList" argument:nil];
754     {
755         ObserveAttachmentUpdatesForScope observer(webView.get());
756         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"test.mp4" contentType:@"video/mp4" data:videoData.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace)];
757         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
758     }
759     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(320, 240)];
760
761     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
762     [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
763     [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
764
765     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(320, 240)];
766     [attachment expectRequestedDataToBe:videoData.get()];
767 }
768
769 TEST(WKAttachmentTests, InPlacePDFAttachmentCutAndPaste)
770 {
771     auto webView = webViewForTestingAttachments();
772     RetainPtr<NSData> pdfData = testPDFData();
773     RetainPtr<_WKAttachment> attachment;
774     {
775         ObserveAttachmentUpdatesForScope observer(webView.get());
776         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"test.pdf" contentType:@"application/pdf" data:pdfData.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace)];
777         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
778         [webView waitForAttachmentElementSizeToBecome:CGSizeMake(130, 29)];
779     }
780
781     [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
782     [webView expectUpdatesAfterCommand:@"Cut" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
783
784     [webView expectUpdatesAfterCommand:@"Paste" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
785     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(130, 29)];
786     [attachment expectRequestedDataToBe:pdfData.get()];
787 }
788
789 TEST(WKAttachmentTests, ChangeAttachmentDataAndFileInformation)
790 {
791     auto webView = webViewForTestingAttachments();
792     RetainPtr<_WKAttachment> attachment;
793     {
794         RetainPtr<NSData> pdfData = testPDFData();
795         ObserveAttachmentUpdatesForScope observer(webView.get());
796         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"test.pdf" contentType:@"application/pdf" data:pdfData.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeAsIcon)];
797         [attachment expectRequestedDataToBe:pdfData.get()];
798         EXPECT_WK_STREQ(@"test.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
799         EXPECT_WK_STREQ(@"application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
800         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
801     }
802     {
803         RetainPtr<NSData> imageData = testImageData();
804         ObserveAttachmentUpdatesForScope observer(webView.get());
805         [attachment synchronouslySetData:imageData.get() newContentType:@"image/png" newFilename:@"icon.png" error:nil];
806         [attachment expectRequestedDataToBe:imageData.get()];
807         EXPECT_WK_STREQ(@"icon.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
808         EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
809         observer.expectAttachmentUpdates(@[], @[]);
810     }
811     {
812         RetainPtr<NSData> textData = testHTMLData();
813         ObserveAttachmentUpdatesForScope observer(webView.get());
814         [attachment synchronouslySetDisplayOptions:displayOptionsWithMode(_WKAttachmentDisplayModeAsIcon) error:nil];
815         // The new content type should be inferred from the file name.
816         [attachment synchronouslySetData:textData.get() newContentType:nil newFilename:@"foo.txt" error:nil];
817         [attachment expectRequestedDataToBe:textData.get()];
818         EXPECT_WK_STREQ(@"foo.txt", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
819         EXPECT_WK_STREQ(@"text/plain", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
820         observer.expectAttachmentUpdates(@[], @[]);
821     }
822     {
823         RetainPtr<NSData> secondTextData = [@"Hello world" dataUsingEncoding:NSUTF8StringEncoding];
824         ObserveAttachmentUpdatesForScope observer(webView.get());
825         // Both the previous file name and type should be inferred.
826         [attachment synchronouslySetData:secondTextData.get() newContentType:nil newFilename:nil error:nil];
827         [attachment expectRequestedDataToBe:secondTextData.get()];
828         EXPECT_WK_STREQ(@"foo.txt", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
829         EXPECT_WK_STREQ(@"text/plain", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
830         observer.expectAttachmentUpdates(@[], @[]);
831     }
832     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
833 }
834
835 TEST(WKAttachmentTests, ChangeAttachmentDataUpdatesWithInPlaceDisplay)
836 {
837     auto webView = webViewForTestingAttachments();
838     RetainPtr<_WKAttachment> attachment;
839     {
840         RetainPtr<NSData> pdfData = testPDFData();
841         ObserveAttachmentUpdatesForScope observer(webView.get());
842         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"test.pdf" contentType:@"application/pdf" data:pdfData.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace)];
843         [webView waitForAttachmentElementSizeToBecome:CGSizeMake(130, 29)];
844         [attachment expectRequestedDataToBe:pdfData.get()];
845         EXPECT_WK_STREQ(@"test.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
846         EXPECT_WK_STREQ(@"application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
847         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
848     }
849     {
850         RetainPtr<NSData> videoData = testVideoData();
851         ObserveAttachmentUpdatesForScope observer(webView.get());
852         [attachment synchronouslySetData:videoData.get() newContentType:@"video/mp4" newFilename:@"test.mp4" error:nil];
853         [webView waitForAttachmentElementSizeToBecome:CGSizeMake(320, 240)];
854         [attachment expectRequestedDataToBe:videoData.get()];
855         EXPECT_WK_STREQ(@"test.mp4", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
856         EXPECT_WK_STREQ(@"video/mp4", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
857         observer.expectAttachmentUpdates(@[], @[]);
858     }
859     {
860         RetainPtr<NSData> imageData = testImageData();
861         ObserveAttachmentUpdatesForScope observer(webView.get());
862         [attachment synchronouslySetData:imageData.get() newContentType:@"image/png" newFilename:@"icon.png" error:nil];
863         [webView waitForAttachmentElementSizeToBecome:CGSizeMake(215, 174)];
864         [attachment expectRequestedDataToBe:imageData.get()];
865         EXPECT_WK_STREQ(@"icon.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
866         EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
867         observer.expectAttachmentUpdates(@[], @[]);
868     }
869     [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
870 }
871
872 TEST(WKAttachmentTests, InsertPastedImageAsAttachment)
873 {
874     platformCopyPNG();
875
876     RetainPtr<_WKAttachment> attachment;
877     auto webView = webViewForTestingAttachments();
878     {
879         ObserveAttachmentUpdatesForScope observer(webView.get());
880         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
881         EXPECT_EQ(1U, observer.observer().inserted.count);
882         attachment = observer.observer().inserted[0];
883     }
884
885     auto size = platformImageWithData([attachment synchronouslyRequestData:nil]).size;
886     EXPECT_EQ(215., size.width);
887     EXPECT_EQ(174., size.height);
888     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
889
890     {
891         ObserveAttachmentUpdatesForScope observer(webView.get());
892         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
893         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
894         observer.expectAttachmentUpdates(@[attachment.get()], @[]);
895     }
896 }
897
898 TEST(WKAttachmentTests, InsertPastedAttributedStringContainingImage)
899 {
900     platformCopyRichTextWithImage();
901
902     RetainPtr<_WKAttachment> attachment;
903     auto webView = webViewForTestingAttachments();
904     {
905         ObserveAttachmentUpdatesForScope observer(webView.get());
906         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
907         EXPECT_EQ(0U, observer.observer().removed.count);
908         EXPECT_EQ(1U, observer.observer().inserted.count);
909         attachment = observer.observer().inserted[0];
910     }
911
912     [attachment expectRequestedDataToBe:testImageData()];
913     EXPECT_WK_STREQ("Lorem ipsum  dolor sit amet.", [webView stringByEvaluatingJavaScript:@"document.body.textContent"]);
914     EXPECT_WK_STREQ("image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
915     EXPECT_WK_STREQ([attachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').uniqueIdentifier"]);
916
917     {
918         ObserveAttachmentUpdatesForScope observer(webView.get());
919         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
920         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
921         observer.expectAttachmentUpdates(@[attachment.get()], @[]);
922     }
923 }
924
925 TEST(WKAttachmentTests, InsertPastedAttributedStringContainingMultipleAttachments)
926 {
927     platformCopyRichTextWithMultipleAttachments();
928
929     RetainPtr<_WKAttachment> imageAttachment;
930     RetainPtr<_WKAttachment> zipAttachment;
931     RetainPtr<_WKAttachment> pdfAttachment;
932     auto webView = webViewForTestingAttachments();
933     {
934         ObserveAttachmentUpdatesForScope observer(webView.get());
935         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
936         EXPECT_EQ(0U, observer.observer().removed.count);
937         EXPECT_EQ(3U, observer.observer().inserted.count);
938         for (_WKAttachment *attachment in observer.observer().inserted) {
939             NSData *data = [attachment synchronouslyRequestData:nil];
940             if ([data isEqualToData:testZIPData()])
941                 zipAttachment = attachment;
942             else if ([data isEqualToData:testPDFData()])
943                 pdfAttachment = attachment;
944             else if ([data isEqualToData:testImageData()])
945                 imageAttachment = attachment;
946         }
947     }
948
949     EXPECT_TRUE(zipAttachment && imageAttachment && pdfAttachment);
950     EXPECT_EQ(3, [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"].integerValue);
951     EXPECT_WK_STREQ("image/png", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
952     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
953
954     NSString *zipAttachmentType = [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[2].getAttribute('type')"];
955 #if USES_MODERN_ATTRIBUTED_STRING_CONVERSION
956     EXPECT_WK_STREQ("application/zip", zipAttachmentType);
957 #else
958     EXPECT_WK_STREQ("application/octet-stream", zipAttachmentType);
959 #endif
960
961     EXPECT_WK_STREQ([imageAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].uniqueIdentifier"]);
962     EXPECT_WK_STREQ([pdfAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].uniqueIdentifier"]);
963     EXPECT_WK_STREQ([zipAttachment uniqueIdentifier], [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[2].uniqueIdentifier"]);
964
965     {
966         ObserveAttachmentUpdatesForScope observer(webView.get());
967         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
968         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
969         NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
970         EXPECT_EQ(3U, removedAttachments.count);
971         EXPECT_TRUE([removedAttachments containsObject:zipAttachment.get()]);
972         EXPECT_TRUE([removedAttachments containsObject:imageAttachment.get()]);
973         EXPECT_TRUE([removedAttachments containsObject:pdfAttachment.get()]);
974     }
975 }
976
977 TEST(WKAttachmentTests, DoNotInsertDataURLImagesAsAttachments)
978 {
979     auto webContentSourceView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]);
980     [webContentSourceView synchronouslyLoadTestPageNamed:@"apple-data-url"];
981     [webContentSourceView selectAll:nil];
982     [webContentSourceView _synchronouslyExecuteEditCommand:@"Copy" argument:nil];
983
984     auto webView = webViewForTestingAttachments();
985     {
986         ObserveAttachmentUpdatesForScope observer(webView.get());
987         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
988         EXPECT_EQ(0U, observer.observer().inserted.count);
989     }
990
991     EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"Boolean(document.querySelector('attachment'))"].boolValue);
992     EXPECT_EQ(1990, [webView stringByEvaluatingJavaScript:@"document.querySelector('img').src.length"].integerValue);
993     EXPECT_WK_STREQ("This is an apple", [webView stringByEvaluatingJavaScript:@"document.body.textContent"]);
994 }
995
996 TEST(WKAttachmentTests, InsertAndRemoveDuplicateAttachment)
997 {
998     auto webView = webViewForTestingAttachments();
999     RetainPtr<NSData> data = testHTMLData();
1000     RetainPtr<_WKAttachment> originalAttachment;
1001     RetainPtr<_WKAttachment> pastedAttachment;
1002     {
1003         ObserveAttachmentUpdatesForScope observer(webView.get());
1004         originalAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:data.get() options:nil];
1005         EXPECT_EQ(0U, observer.observer().removed.count);
1006         observer.expectAttachmentUpdates(@[], @[originalAttachment.get()]);
1007     }
1008     [webView selectAll:nil];
1009     [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
1010     [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
1011     {
1012         ObserveAttachmentUpdatesForScope observer(webView.get());
1013         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1014         EXPECT_EQ(0U, observer.observer().removed.count);
1015         EXPECT_EQ(1U, observer.observer().inserted.count);
1016         pastedAttachment = observer.observer().inserted.firstObject;
1017         EXPECT_FALSE([pastedAttachment isEqual:originalAttachment.get()]);
1018     }
1019     {
1020         ObserveAttachmentUpdatesForScope observer(webView.get());
1021         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1022         observer.expectAttachmentUpdates(@[pastedAttachment.get()], @[]);
1023         [originalAttachment expectRequestedDataToBe:data.get()];
1024     }
1025     {
1026         ObserveAttachmentUpdatesForScope observer(webView.get());
1027         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1028         observer.expectAttachmentUpdates(@[originalAttachment.get()], @[]);
1029     }
1030 }
1031
1032 TEST(WKAttachmentTests, InsertDuplicateAttachmentAndUpdateData)
1033 {
1034     auto webView = webViewForTestingAttachments();
1035     RetainPtr<NSData> originalData = testHTMLData();
1036     RetainPtr<_WKAttachment> originalAttachment;
1037     RetainPtr<_WKAttachment> pastedAttachment;
1038     {
1039         ObserveAttachmentUpdatesForScope observer(webView.get());
1040         originalAttachment = [webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:originalData.get() options:nil];
1041         EXPECT_EQ(0U, observer.observer().removed.count);
1042         observer.expectAttachmentUpdates(@[], @[originalAttachment.get()]);
1043     }
1044     [webView selectAll:nil];
1045     [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
1046     [webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
1047     {
1048         ObserveAttachmentUpdatesForScope observer(webView.get());
1049         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1050         EXPECT_EQ(0U, observer.observer().removed.count);
1051         EXPECT_EQ(1U, observer.observer().inserted.count);
1052         pastedAttachment = observer.observer().inserted.firstObject;
1053         EXPECT_FALSE([pastedAttachment isEqual:originalAttachment.get()]);
1054     }
1055     auto updatedData = retainPtr([@"HELLO WORLD" dataUsingEncoding:NSUTF8StringEncoding]);
1056     [originalAttachment synchronouslySetData:updatedData.get() newContentType:nil newFilename:nil error:nil];
1057     [originalAttachment expectRequestedDataToBe:updatedData.get()];
1058     [pastedAttachment expectRequestedDataToBe:originalData.get()];
1059 }
1060
1061 #pragma mark - Platform-specific tests
1062
1063 #if PLATFORM(MAC)
1064
1065 TEST(WKAttachmentTestsMac, InsertPastedFileURLsAsAttachments)
1066 {
1067     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1068     [pasteboard clearContents];
1069     [pasteboard declareTypes:@[NSFilenamesPboardType] owner:nil];
1070     [pasteboard setPropertyList:@[testPDFFileURL().path, testImageFileURL().path] forType:NSFilenamesPboardType];
1071
1072     RetainPtr<NSArray<_WKAttachment *>> insertedAttachments;
1073     auto webView = webViewForTestingAttachments();
1074     {
1075         ObserveAttachmentUpdatesForScope observer(webView.get());
1076         [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
1077         insertedAttachments = [observer.observer() inserted];
1078         EXPECT_EQ(2U, [insertedAttachments count]);
1079     }
1080
1081     NSArray<NSData *> *expectedAttachmentData = @[ testPDFData(), testImageData() ];
1082     EXPECT_TRUE([expectedAttachmentData containsObject:[[insertedAttachments firstObject] synchronouslyRequestData:nil]]);
1083     EXPECT_TRUE([expectedAttachmentData containsObject:[[insertedAttachments lastObject] synchronouslyRequestData:nil]]);
1084     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1085     EXPECT_WK_STREQ("test.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
1086     EXPECT_WK_STREQ("image/png", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
1087     EXPECT_WK_STREQ("icon.png", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
1088
1089     for (_WKAttachment *attachment in insertedAttachments.get()) {
1090         _WKAttachmentInfo *info = [attachment synchronouslyRequestInfo:nil];
1091         EXPECT_GT(info.filePath.length, 0U);
1092     }
1093
1094     {
1095         ObserveAttachmentUpdatesForScope observer(webView.get());
1096         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1097         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1098         NSArray<_WKAttachment *> *removedAttachments = [observer.observer() removed];
1099         EXPECT_EQ(2U, removedAttachments.count);
1100         EXPECT_TRUE([removedAttachments containsObject:[insertedAttachments firstObject]]);
1101         EXPECT_TRUE([removedAttachments.lastObject isEqual:[insertedAttachments lastObject]]);
1102     }
1103 }
1104
1105 #endif // PLATFORM(MAC)
1106
1107 #if PLATFORM(IOS)
1108
1109 TEST(WKAttachmentTestsIOS, InsertDroppedImageAsAttachment)
1110 {
1111     auto webView = webViewForTestingAttachments();
1112     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1113     auto item = adoptNS([[NSItemProvider alloc] init]);
1114     [item registerData:testImageData() type:(NSString *)kUTTypePNG];
1115     [draggingSimulator setExternalItemProviders:@[ item.get() ]];
1116     [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1117
1118     EXPECT_EQ(1U, [draggingSimulator insertedAttachments].count);
1119     EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
1120     auto attachment = retainPtr([draggingSimulator insertedAttachments].firstObject);
1121     [attachment expectRequestedDataToBe:testImageData()];
1122     EXPECT_WK_STREQ("public.png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1123
1124     {
1125         ObserveAttachmentUpdatesForScope observer(webView.get());
1126         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1127         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1128         observer.expectAttachmentUpdates(@[attachment.get()], @[]);
1129     }
1130 }
1131
1132 TEST(WKAttachmentTestsIOS, InsertDroppedAttributedStringContainingAttachment)
1133 {
1134     auto webView = webViewForTestingAttachments();
1135     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1136     auto image = adoptNS([[NSTextAttachment alloc] initWithData:testImageData() ofType:(NSString *)kUTTypePNG]);
1137     auto item = adoptNS([[NSItemProvider alloc] init]);
1138     [item registerObject:[NSAttributedString attributedStringWithAttachment:image.get()] visibility:NSItemProviderRepresentationVisibilityAll];
1139
1140     [draggingSimulator setExternalItemProviders:@[ item.get() ]];
1141     [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1142
1143     EXPECT_EQ(1U, [draggingSimulator insertedAttachments].count);
1144     EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
1145     auto attachment = retainPtr([draggingSimulator insertedAttachments].firstObject);
1146
1147     auto size = platformImageWithData([attachment synchronouslyRequestData:nil]).size;
1148     EXPECT_EQ(215., size.width);
1149     EXPECT_EQ(174., size.height);
1150     EXPECT_WK_STREQ("image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1151
1152     {
1153         ObserveAttachmentUpdatesForScope observer(webView.get());
1154         [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
1155         [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
1156         observer.expectAttachmentUpdates(@[attachment.get()], @[]);
1157     }
1158 }
1159
1160 TEST(WKAttachmentTestsIOS, InsertDroppedRichAndPlainTextFilesAsAttachments)
1161 {
1162     // Here, both rich text and plain text are content types that WebKit already understands how to insert in editable
1163     // areas in the absence of attachment elements. However, due to the explicitly set attachment presentation style
1164     // on the item providers, we should instead treat them as dropped files and insert attachment elements.
1165     // This exercises the scenario of dragging rich and plain text files from Files to Mail.
1166     auto richTextItem = adoptNS([[NSItemProvider alloc] init]);
1167     auto richText = adoptNS([[NSAttributedString alloc] initWithString:@"Hello world" attributes:@{ NSFontAttributeName: [UIFont boldSystemFontOfSize:12] }]);
1168     [richTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1169     [richTextItem registerObject:richText.get() visibility:NSItemProviderRepresentationVisibilityAll];
1170     [richTextItem setSuggestedName:@"hello.rtf"];
1171
1172     auto plainTextItem = adoptNS([[NSItemProvider alloc] init]);
1173     [plainTextItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1174     [plainTextItem registerObject:@"Hello world" visibility:NSItemProviderRepresentationVisibilityAll];
1175     [plainTextItem setSuggestedName:@"world.txt"];
1176
1177     auto webView = webViewForTestingAttachments();
1178     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1179     [draggingSimulator setExternalItemProviders:@[ richTextItem.get(), plainTextItem.get() ]];
1180     [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1181
1182     EXPECT_EQ(2U, [draggingSimulator insertedAttachments].count);
1183     EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
1184
1185     for (_WKAttachment *attachment in [draggingSimulator insertedAttachments]) {
1186         NSError *error = nil;
1187         EXPECT_GT([attachment synchronouslyRequestData:&error].length, 0U);
1188         EXPECT_TRUE(!error);
1189         if (error)
1190             NSLog(@"Error: %@", error);
1191     }
1192
1193     EXPECT_EQ(2, [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"].intValue);
1194     EXPECT_WK_STREQ("hello.rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
1195     EXPECT_WK_STREQ("text/rtf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1196     EXPECT_WK_STREQ("world.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
1197     EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
1198 }
1199
1200 TEST(WKAttachmentTestsIOS, InsertDroppedZipArchiveAsAttachment)
1201 {
1202     // Since WebKit doesn't have any default DOM representation for ZIP archives, we should fall back to inserting
1203     // attachment elements. This exercises the flow of dragging a ZIP file from an app that doesn't specify a preferred
1204     // presentation style (e.g. Notes) into Mail.
1205     auto item = adoptNS([[NSItemProvider alloc] init]);
1206     NSData *data = testZIPData();
1207     [item registerData:data type:(NSString *)kUTTypeZipArchive];
1208     [item setSuggestedName:@"archive.zip"];
1209
1210     auto webView = webViewForTestingAttachments();
1211     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1212     [draggingSimulator setExternalItemProviders:@[ item.get() ]];
1213     [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1214
1215     EXPECT_EQ(1U, [draggingSimulator insertedAttachments].count);
1216     EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
1217     [[draggingSimulator insertedAttachments].firstObject expectRequestedDataToBe:data];
1218     EXPECT_EQ(1, [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment').length"].intValue);
1219     EXPECT_WK_STREQ("archive.zip", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1220     EXPECT_WK_STREQ("application/zip", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1221 }
1222
1223 TEST(WKAttachmentTestsIOS, InsertDroppedItemProvidersInOrder)
1224 {
1225     // Tests that item providers are inserted in the order they are specified. In this case, the two inserted attachments
1226     // should be separated by a link.
1227     auto firstAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
1228     [firstAttachmentItem setPreferredPresentationStyle:UIPreferredPresentationStyleAttachment];
1229     [firstAttachmentItem registerObject:@"FIRST" visibility:NSItemProviderRepresentationVisibilityAll];
1230     [firstAttachmentItem setSuggestedName:@"first.txt"];
1231
1232     auto inlineTextItem = adoptNS([[NSItemProvider alloc] init]);
1233     auto appleURL = retainPtr([NSURL URLWithString:@"https://www.apple.com/"]);
1234     [inlineTextItem registerObject:appleURL.get() visibility:NSItemProviderRepresentationVisibilityAll];
1235
1236     auto secondAttachmentItem = adoptNS([[NSItemProvider alloc] init]);
1237     [secondAttachmentItem registerData:testPDFData() type:(NSString *)kUTTypePDF];
1238     [secondAttachmentItem setSuggestedName:@"second.pdf"];
1239
1240     auto webView = webViewForTestingAttachments();
1241     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1242     [draggingSimulator setExternalItemProviders:@[ firstAttachmentItem.get(), inlineTextItem.get(), secondAttachmentItem.get() ]];
1243     [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1244
1245     EXPECT_EQ(2U, [draggingSimulator insertedAttachments].count);
1246     EXPECT_EQ(0U, [draggingSimulator removedAttachments].count);
1247
1248     for (_WKAttachment *attachment in [draggingSimulator insertedAttachments]) {
1249         NSError *error = nil;
1250         EXPECT_GT([attachment synchronouslyRequestData:&error].length, 0U);
1251         EXPECT_TRUE(!error);
1252         if (error)
1253             NSLog(@"Error: %@", error);
1254     }
1255
1256     NSArray *observedElementTags = (NSArray *)[webView objectByEvaluatingJavaScript:@"Array.from(document.body.children).map(e => e.tagName)"];
1257     NSArray *expectedElementTags = @[ @"ATTACHMENT", @"A", @"ATTACHMENT" ];
1258     EXPECT_TRUE([observedElementTags isEqualToArray:expectedElementTags]);
1259     if (![observedElementTags isEqualToArray:expectedElementTags])
1260         NSLog(@"Observed elements: %@ did not match expectations: %@", observedElementTags, expectedElementTags);
1261
1262     EXPECT_WK_STREQ("first.txt", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('title')"]);
1263     EXPECT_WK_STREQ("text/plain", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[0].getAttribute('type')"]);
1264     EXPECT_WK_STREQ([appleURL absoluteString], [webView valueOfAttribute:@"href" forQuerySelector:@"a"]);
1265     EXPECT_WK_STREQ("second.pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('title')"]);
1266     EXPECT_WK_STREQ("application/pdf", [webView stringByEvaluatingJavaScript:@"document.querySelectorAll('attachment')[1].getAttribute('type')"]);
1267 }
1268
1269 TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsFile)
1270 {
1271     auto item = adoptNS([[NSItemProvider alloc] init]);
1272     auto data = retainPtr(testPDFData());
1273     [item registerData:data.get() type:(NSString *)kUTTypePDF];
1274     [item setSuggestedName:@"document.pdf"];
1275
1276     auto webView = webViewForTestingAttachments();
1277     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1278     [draggingSimulator setExternalItemProviders:@[ item.get() ]];
1279     [draggingSimulator runFrom:CGPointZero to:CGPointMake(50, 50)];
1280
1281     // First, verify that the attachment was successfully dropped.
1282     EXPECT_EQ(1U, [draggingSimulator insertedAttachments].count);
1283     _WKAttachment *attachment = [draggingSimulator insertedAttachments].firstObject;
1284     [attachment expectRequestedDataToBe:data.get()];
1285     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1286     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1287
1288     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
1289     [draggingSimulator setExternalItemProviders:@[ ]];
1290     [draggingSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
1291
1292     // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
1293     EXPECT_EQ(1U, [draggingSimulator sourceItemProviders].count);
1294     NSItemProvider *itemProvider = [draggingSimulator sourceItemProviders].firstObject;
1295     EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
1296     [itemProvider expectType:(NSString *)kUTTypePDF withData:data.get()];
1297     EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
1298     [draggingSimulator endDataTransfer];
1299 }
1300
1301 TEST(WKAttachmentTestsIOS, DragAttachmentInsertedAsData)
1302 {
1303     auto webView = webViewForTestingAttachments();
1304     auto data = retainPtr(testPDFData());
1305     RetainPtr<_WKAttachment> attachment;
1306     {
1307         ObserveAttachmentUpdatesForScope observer(webView.get());
1308         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"document.pdf" contentType:@"application/pdf" data:data.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeAsIcon)];
1309         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
1310     }
1311
1312     // First, verify that the attachment was successfully inserted from raw data.
1313     [attachment expectRequestedDataToBe:data.get()];
1314     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1315     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1316
1317     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
1318     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1319     [draggingSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
1320
1321     // Next, verify that dragging the attachment produces an item provider with a PDF attachment.
1322     EXPECT_EQ(1U, [draggingSimulator sourceItemProviders].count);
1323     NSItemProvider *itemProvider = [draggingSimulator sourceItemProviders].firstObject;
1324     EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
1325     [itemProvider expectType:(NSString *)kUTTypePDF withData:data.get()];
1326     EXPECT_WK_STREQ("document.pdf", [itemProvider suggestedName]);
1327     [draggingSimulator endDataTransfer];
1328 }
1329
1330 TEST(WKAttachmentTestsIOS, DragInPlaceVideoAttachmentElement)
1331 {
1332     auto webView = webViewForTestingAttachments();
1333     auto data = retainPtr(testVideoData());
1334     RetainPtr<_WKAttachment> attachment;
1335     {
1336         ObserveAttachmentUpdatesForScope observer(webView.get());
1337         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"video.mp4" contentType:@"video/mp4" data:data.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace)];
1338         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
1339     }
1340
1341     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(320, 240)];
1342     [attachment expectRequestedDataToBe:data.get()];
1343     EXPECT_WK_STREQ("video.mp4", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1344     EXPECT_WK_STREQ("video/mp4", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1345
1346     [webView evaluateJavaScript:@"getSelection().removeAllRanges()" completionHandler:nil];
1347     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1348     [draggingSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(-100, -100)];
1349
1350     EXPECT_EQ(1U, [draggingSimulator sourceItemProviders].count);
1351     NSItemProvider *itemProvider = [draggingSimulator sourceItemProviders].firstObject;
1352     EXPECT_EQ(UIPreferredPresentationStyleAttachment, itemProvider.preferredPresentationStyle);
1353     [itemProvider expectType:(NSString *)kUTTypeMPEG4 withData:data.get()];
1354     EXPECT_WK_STREQ("video.mp4", [itemProvider suggestedName]);
1355     [draggingSimulator endDataTransfer];
1356 }
1357
1358 TEST(WKAttachmentTestsIOS, MoveAttachmentElementAsIconByDragging)
1359 {
1360     auto webView = webViewForTestingAttachments();
1361     auto data = retainPtr(testPDFData());
1362     RetainPtr<_WKAttachment> attachment;
1363     {
1364         ObserveAttachmentUpdatesForScope observer(webView.get());
1365         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"document.pdf" contentType:@"application/pdf" data:data.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeAsIcon)];
1366         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
1367     }
1368
1369     [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
1370     [webView _executeEditCommand:@"InsertHTML" argument:@"<strong>text</strong>" completion:nil];
1371     [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
1372     [webView expectElementTag:@"ATTACHMENT" toComeBefore:@"STRONG"];
1373
1374     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1375     [draggingSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(25, 125)];
1376
1377     attachment = [[draggingSimulator insertedAttachments] firstObject];
1378     [attachment expectRequestedDataToBe:data.get()];
1379     EXPECT_WK_STREQ("document.pdf", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1380     EXPECT_WK_STREQ("application/pdf", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1381
1382     [webView expectElementTag:@"STRONG" toComeBefore:@"ATTACHMENT"];
1383     [draggingSimulator endDataTransfer];
1384 }
1385
1386 TEST(WKAttachmentTestsIOS, MoveInPlaceAttachmentElementByDragging)
1387 {
1388     auto webView = webViewForTestingAttachments();
1389     auto data = retainPtr(testImageData());
1390     RetainPtr<_WKAttachment> attachment;
1391     {
1392         ObserveAttachmentUpdatesForScope observer(webView.get());
1393         attachment = [webView synchronouslyInsertAttachmentWithFilename:@"icon.png" contentType:@"image/png" data:data.get() options:displayOptionsWithMode(_WKAttachmentDisplayModeInPlace)];
1394         observer.expectAttachmentUpdates(@[], @[attachment.get()]);
1395     }
1396
1397     [webView waitForAttachmentElementSizeToBecome:CGSizeMake(215, 174)];
1398     [webView _executeEditCommand:@"InsertParagraph" argument:nil completion:nil];
1399     [webView _executeEditCommand:@"InsertHTML" argument:@"<strong>text</strong>" completion:nil];
1400     [webView _synchronouslyExecuteEditCommand:@"InsertParagraph" argument:nil];
1401     [webView expectElementTag:@"ATTACHMENT" toComeBefore:@"STRONG"];
1402
1403     auto draggingSimulator = adoptNS([[DataInteractionSimulator alloc] initWithWebView:webView.get()]);
1404     [draggingSimulator runFrom:CGPointMake(25, 25) to:CGPointMake(25, 125)];
1405
1406     attachment = [[draggingSimulator insertedAttachments] firstObject];
1407     [attachment expectRequestedDataToBe:data.get()];
1408     EXPECT_WK_STREQ("icon.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
1409     EXPECT_WK_STREQ("image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
1410
1411     [webView expectElementTag:@"STRONG" toComeBefore:@"ATTACHMENT"];
1412     [draggingSimulator endDataTransfer];
1413 }
1414
1415 #endif // PLATFORM(IOS)
1416
1417 } // namespace TestWebKitAPI
1418
1419 #endif // WK_API_ENABLED && !PLATFORM(WATCHOS) && !PLATFORM(TVOS)