2 * Copyright (C) 2005-2018 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #import "DumpRenderTreeDraggingInfo.h"
34 #import "DumpRenderTree.h"
35 #import "DumpRenderTreeFileDraggingSource.h"
36 #import "DumpRenderTreePasteboard.h"
37 #import "EventSendingController.h"
38 #import <WebKit/WebKit.h>
39 #import <wtf/RetainPtr.h>
41 @interface NSDraggingItem ()
42 - (void)setItem:(id)item;
45 @interface DumpRenderTreeFilePromiseReceiver : NSFilePromiseReceiver {
46 RetainPtr<NSArray<NSString *>> _promisedUTIs;
47 RetainPtr<NSMutableArray<NSURL *>> _destinationURLs;
48 DumpRenderTreeFileDraggingSource *_draggingSource;
51 - (instancetype)initWithPromisedUTIs:(NSArray<NSString *> *)promisedUTIs;
53 @property (nonatomic, retain) DumpRenderTreeFileDraggingSource *draggingSource;
57 @implementation DumpRenderTreeFilePromiseReceiver
59 @synthesize draggingSource=_draggingSource;
61 - (instancetype)initWithPromisedUTIs:(NSArray<NSString *> *)promisedUTIs
63 if (!(self = [super init]))
66 _promisedUTIs = adoptNS([promisedUTIs copy]);
67 _destinationURLs = adoptNS([NSMutableArray new]);
71 - (NSArray<NSString *> *)fileTypes
73 return _promisedUTIs.get();
76 - (NSArray<NSString *> *)fileNames
78 NSMutableArray *fileNames = [NSMutableArray arrayWithCapacity:[_destinationURLs count]];
79 for (NSURL *url in _destinationURLs.get())
80 [fileNames addObject:url.lastPathComponent];
86 // WebKit does not delete promised files it receives into NSTemporaryDirectory() (it should!),
87 // so we need to. Failing to do so could result in unpredictable file names in a subsequent test
88 // that promises a file with the same name as one of these destination URLs.
90 for (NSURL *destinationURL in _destinationURLs.get()) {
91 assert([destinationURL.path hasPrefix:NSTemporaryDirectory()]);
92 [NSFileManager.defaultManager removeItemAtURL:destinationURL error:nil];
95 [_draggingSource release];
99 static NSURL *copyFile(NSURL *sourceURL, NSURL *destinationDirectory, NSError *&error)
101 // Emulate how CFPasteboard finds unique destination file names by inserting " 2", " 3", and so
102 // on between the file name's base and extension until a new file is successfully created in
103 // the destination directory.
105 NSUInteger number = 2;
106 NSString *fileName = sourceURL.lastPathComponent;
107 NSURL *destinationURL = [NSURL fileURLWithPath:fileName relativeToURL:destinationDirectory];
108 while (![NSFileManager.defaultManager copyItemAtURL:sourceURL toURL:destinationURL error:&error]) {
109 if (error.domain != NSCocoaErrorDomain || error.code != NSFileWriteFileExistsError)
112 NSString *newFileName = [NSString stringWithFormat:@"%@ %lu.%@", fileName.stringByDeletingPathExtension, (unsigned long)number++, fileName.pathExtension];
113 destinationURL = [NSURL fileURLWithPath:newFileName relativeToURL:destinationDirectory];
117 return destinationURL;
120 - (void)receivePromisedFilesAtDestination:(NSURL *)destinationDirectory options:(NSDictionary *)options operationQueue:(NSOperationQueue *)operationQueue reader:(void (^)(NSURL *fileURL, NSError * __nullable errorOrNil))reader
122 // Layout tests need files to be received in a predictable order, so execute operations in serial.
123 operationQueue.maxConcurrentOperationCount = 1;
125 NSArray<NSURL *> *sourceURLs = _draggingSource.promisedFileURLs;
126 for (NSURL *sourceURL in sourceURLs) {
127 [operationQueue addOperationWithBlock:^{
128 NSError *error = nil;
129 NSURL *destinationURL = copyFile(sourceURL, destinationDirectory, error);
130 if (destinationURL) {
131 dispatch_async(dispatch_get_main_queue(), ^{
132 [_destinationURLs addObject:destinationURL];
136 reader(destinationURL, error);
143 @implementation DumpRenderTreeDraggingInfo
145 - (id)initWithImage:(NSImage *)anImage offset:(NSSize)o pasteboard:(NSPasteboard *)pboard source:(id)source
147 draggedImage = [anImage retain];
148 draggingPasteboard = [pboard retain];
149 draggingSource = [source retain];
157 [draggedImage release];
158 [draggingPasteboard release];
159 [draggingSource release];
163 - (NSWindow *)draggingDestinationWindow
165 return [[mainFrame webView] window];
168 - (NSDragOperation)draggingSourceOperationMask
170 return [draggingSource draggingSourceOperationMaskForLocal:YES];
173 - (NSPoint)draggingLocation
175 return lastMousePosition;
178 - (NSPoint)draggedImageLocation
180 return NSMakePoint(lastMousePosition.x + offset.width, lastMousePosition.y + offset.height);
183 - (NSImage *)draggedImage
188 - (NSPasteboard *)draggingPasteboard
190 return draggingPasteboard;
195 return draggingSource;
198 - (int)draggingSequenceNumber
200 NSLog(@"DumpRenderTree doesn't support draggingSequenceNumber");
204 - (void)slideDraggedImageTo:(NSPoint)screenPoint
206 NSLog(@"DumpRenderTree doesn't support slideDraggedImageTo:");
209 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
211 NSLog(@"DumpRenderTree doesn't support namesOfPromisedFilesDroppedAtDestination:");
215 - (NSDraggingFormation)draggingFormation
217 return NSDraggingFormationDefault;
220 - (void)setDraggingFormation:(NSDraggingFormation)formation
225 - (BOOL)animatesToDestination
230 - (void)setAnimatesToDestination:(BOOL)flag
235 - (NSInteger)numberOfValidItemsForDrop
240 - (void)setNumberOfValidItemsForDrop:(NSInteger)number
245 static NSMutableArray<NSFilePromiseReceiver *> *allFilePromiseReceivers()
247 static NSMutableArray<NSFilePromiseReceiver *> *allReceivers = [[NSMutableArray alloc] init];
251 + (void)clearAllFilePromiseReceivers
253 [allFilePromiseReceivers() removeAllObjects];
256 - (void)enumerateDraggingItemsWithOptions:(NSEnumerationOptions)enumOptions forView:(NSView *)view classes:(NSArray *)classArray searchOptions:(NSDictionary *)searchOptions usingBlock:(void (^)(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop))block
258 assert(!enumOptions);
259 assert(!searchOptions.count);
262 for (Class classObject in classArray) {
263 if (classObject != NSFilePromiseReceiver.class)
266 id promisedUTIs = [draggingPasteboard propertyListForType:NSFilesPromisePboardType];
267 if (![promisedUTIs isKindOfClass:NSArray.class])
270 for (id object in promisedUTIs) {
271 if (![object isKindOfClass:NSString.class])
275 auto receiver = adoptNS([[DumpRenderTreeFilePromiseReceiver alloc] initWithPromisedUTIs:promisedUTIs]);
276 [receiver setDraggingSource:draggingSource];
277 [allFilePromiseReceivers() addObject:receiver.get()];
279 auto item = adoptNS([NSDraggingItem new]);
280 [item setItem:receiver.get()];
282 block(item.get(), 0, &stop);
288 -(NSSpringLoadingHighlight)springLoadingHighlight
290 return NSSpringLoadingHighlightNone;
293 - (void)resetSpringLoading
299 #endif // !PLATFORM(IOS)