1/*
2 * Copyright (C) 2005, 2006, 2007 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 *
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.
16 *
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.
27 */
28
29#if !PLATFORM(IOS)
30
31#import "WebNSPasteboardExtras.h"
32
33#import "DOMElementInternal.h"
34#import "WebArchive.h"
35#import "WebFrameInternal.h"
36#import "WebHTMLViewInternal.h"
37#import "WebNSURLExtras.h"
38#import "WebResourcePrivate.h"
39#import "WebURLsWithTitles.h"
40#import "WebViewPrivate.h"
41#import <WebCore/CachedImage.h>
42#import <WebCore/Element.h>
43#import <WebCore/Image.h>
44#import <WebCore/MIMETypeRegistry.h>
45#import <WebCore/RenderImage.h>
46#import <WebKitLegacy/DOMExtensions.h>
47#import <WebKitLegacy/DOMPrivate.h>
48#import <WebKitSystemInterface.h>
49#import <wtf/Assertions.h>
50#import <wtf/RetainPtr.h>
51#import <wtf/StdLibExtras.h>
52
53using namespace WebCore;
54
55NSString *WebURLPboardType = @"public.url";
56NSString *WebURLNamePboardType = @"public.url-name";
57
58@implementation NSPasteboard (WebExtras)
59
60+ (NSArray *)_web_writableTypesForURL
61{
62    DEPRECATED_DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, types, ([[NSArray alloc] initWithObjects:
63        WebURLsWithTitlesPboardType,
64        NSURLPboardType,
65        WebURLPboardType,
66        WebURLNamePboardType,
67        NSStringPboardType,
68        nil]));
69    return types.get();
70}
71
72static inline NSArray *_createWritableTypesForImageWithoutArchive()
73{
74    NSMutableArray *types = [[NSMutableArray alloc] initWithObjects:NSTIFFPboardType, nil];
75    [types addObjectsFromArray:[NSPasteboard _web_writableTypesForURL]];
76    return types;
77}
78
79static NSArray *_writableTypesForImageWithoutArchive (void)
80{
81    DEPRECATED_DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, types, (_createWritableTypesForImageWithoutArchive()));
82    return types.get();
83}
84
85static inline NSArray *_createWritableTypesForImageWithArchive()
86{
87    NSMutableArray *types = [_writableTypesForImageWithoutArchive() mutableCopy];
88    [types addObject:NSRTFDPboardType];
89    [types addObject:WebArchivePboardType];
90    return types;
91}
92
93static NSArray *_writableTypesForImageWithArchive (void)
94{
95    DEPRECATED_DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, types, (_createWritableTypesForImageWithArchive()));
96    return types.get();
97}
98
99+ (NSArray *)_web_writableTypesForImageIncludingArchive:(BOOL)hasArchive
100{
101    return hasArchive
102        ? _writableTypesForImageWithArchive()
103        : _writableTypesForImageWithoutArchive();
104}
105
106+ (NSArray *)_web_dragTypesForURL
107{
108    return [NSArray arrayWithObjects:
109        WebURLsWithTitlesPboardType,
110        NSURLPboardType,
111        WebURLPboardType,
112        WebURLNamePboardType,
113        NSStringPboardType,
114        NSFilenamesPboardType,
115        nil];
116}
117
118- (NSURL *)_web_bestURL
119{
120    NSArray *types = [self types];
121
122    if ([types containsObject:NSURLPboardType]) {
123        NSURL *URLFromPasteboard = [NSURL URLFromPasteboard:self];
124        NSString *scheme = [URLFromPasteboard scheme];
125        if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) {
126            return [URLFromPasteboard _webkit_canonicalize];
127        }
128    }
129
130    if ([types containsObject:NSStringPboardType]) {
131        NSString *URLString = [self stringForType:NSStringPboardType];
132        if ([URLString _webkit_looksLikeAbsoluteURL]) {
133            NSURL *URL = [[NSURL _web_URLWithUserTypedString:URLString] _webkit_canonicalize];
134            if (URL) {
135                return URL;
136            }
137        }
138    }
139
140    if ([types containsObject:NSFilenamesPboardType]) {
141        NSArray *files = [self propertyListForType:NSFilenamesPboardType];
142        // FIXME: Maybe it makes more sense to allow multiple files and only use the first one?
143        if ([files count] == 1) {
144            NSString *file = [files objectAtIndex:0];
145            // FIXME: We are filtering out directories because that's what the original code used to
146            // do. Without this check, if the URL points to a local directory, Safari will open the
147            // parent directory of the directory in Finder. This check should go away as soon as
148            // possible.
149            BOOL isDirectory;
150            if ([[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory] && isDirectory)
151                return nil;
152            return [[NSURL fileURLWithPath:file] _webkit_canonicalize];
153        }
154    }
155
156    return nil;
157}
158
159- (void)_web_writeURL:(NSURL *)URL andTitle:(NSString *)title types:(NSArray *)types
160{
161    ASSERT(URL);
162
163    if ([title length] == 0) {
164        title = [[URL path] lastPathComponent];
165        if ([title length] == 0)
166            title = [URL _web_userVisibleString];
167    }
168
169    if ([types containsObject:NSURLPboardType])
170        [URL writeToPasteboard:self];
171    if ([types containsObject:WebURLPboardType])
172        [self setString:[URL _web_originalDataAsString] forType:WebURLPboardType];
173    if ([types containsObject:WebURLNamePboardType])
174        [self setString:title forType:WebURLNamePboardType];
175    if ([types containsObject:NSStringPboardType])
176        [self setString:[URL _web_userVisibleString] forType:NSStringPboardType];
177    if ([types containsObject:WebURLsWithTitlesPboardType])
178        [WebURLsWithTitles writeURLs:[NSArray arrayWithObject:URL] andTitles:[NSArray arrayWithObject:title] toPasteboard:self];
179}
180
181+ (int)_web_setFindPasteboardString:(NSString *)string withOwner:(id)owner
182{
183    NSPasteboard *findPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
184    [findPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:owner];
185    [findPasteboard setString:string forType:NSStringPboardType];
186    return [findPasteboard changeCount];
187}
188
189- (void)_web_writeFileWrapperAsRTFDAttachment:(NSFileWrapper *)wrapper
190{
191    NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:wrapper];
192
193    NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment];
194    [attachment release];
195
196    NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:nil];
197    [self setData:RTFDData forType:NSRTFDPboardType];
198}
199
200
201- (void)_web_writePromisedRTFDFromArchive:(WebArchive*)archive containsImage:(BOOL)containsImage
202{
203    ASSERT(archive);
204    // This image data is either the only subresource of an archive (HTML image case)
205    // or the main resource (standalone image case).
206    NSArray *subresources = [archive subresources];
207    WebResource *resource = [archive mainResource];
208    if (containsImage && [subresources count] > 0
209        && MIMETypeRegistry::isSupportedImageResourceMIMEType([[subresources objectAtIndex:0] MIMEType]))
210        resource = (WebResource *)[subresources objectAtIndex:0];
211    ASSERT(resource != nil);
212
213    ASSERT(!containsImage || MIMETypeRegistry::isSupportedImageResourceMIMEType([resource MIMEType]));
214    if (!containsImage || MIMETypeRegistry::isSupportedImageResourceMIMEType([resource MIMEType]))
215        [self _web_writeFileWrapperAsRTFDAttachment:[resource _fileWrapperRepresentation]];
216
217}
218
219static CachedImage* imageFromElement(DOMElement *domElement)
220{
221    Element* element = core(domElement);
222    if (!element)
223        return 0;
224
225    RenderObject* renderer = element->renderer();
226    RenderImage* imageRenderer = toRenderImage(renderer);
227    if (!imageRenderer->cachedImage() || imageRenderer->cachedImage()->errorOccurred())
228        return 0;
229    return imageRenderer->cachedImage();
230}
231
232- (void)_web_writeImage:(NSImage *)image
233                element:(DOMElement *)element
234                    URL:(NSURL *)URL
235                  title:(NSString *)title
236                archive:(WebArchive *)archive
237                  types:(NSArray *)types
238                 source:(WebHTMLView *)source
239{
240    ASSERT(image || element);
241    ASSERT(URL);
242
243    [self _web_writeURL:URL andTitle:title types:types];
244
245    if ([types containsObject:NSTIFFPboardType]) {
246        if (image)
247            [self setData:[image TIFFRepresentation] forType:NSTIFFPboardType];
248        else if (source && element)
249            [source setPromisedDragTIFFDataSource:imageFromElement(element)];
250        else if (element)
251            [self setData:[element _imageTIFFRepresentation] forType:NSTIFFPboardType];
252    }
253
254    if (archive) {
255        if ([types containsObject:WebArchivePboardType])
256            [self setData:[archive data] forType:WebArchivePboardType];
257        return;
258    }
259
260    // We should not have declared types that we aren't going to write (4031826).
261    ASSERT(![types containsObject:NSRTFDPboardType]);
262    ASSERT(![types containsObject:WebArchivePboardType]);
263}
264
265- (id)_web_declareAndWriteDragImageForElement:(DOMElement *)element
266                                       URL:(NSURL *)URL
267                                     title:(NSString *)title
268                                   archive:(WebArchive *)archive
269                                    source:(WebHTMLView *)source
270{
271    ASSERT(self == [NSPasteboard pasteboardWithName:NSDragPboard]);
272
273    NSString *extension = @"";
274    if (RenderObject* renderer = core(element)->renderer()) {
275        if (renderer->isRenderImage()) {
276            if (CachedImage* image = toRenderImage(renderer)->cachedImage()) {
277                extension = image->image()->filenameExtension();
278                if (![extension length])
279                    return 0;
280            }
281        }
282    }
283
284    NSMutableArray *types = [[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil];
285    [types addObjectsFromArray:[NSPasteboard _web_writableTypesForImageIncludingArchive:(archive != nil)]];
286    [self declareTypes:types owner:source];
287    [self _web_writeImage:nil element:element URL:URL title:title archive:archive types:types source:source];
288    [types release];
289
290    NSArray *extensions = [[NSArray alloc] initWithObjects:extension, nil];
291    [self setPropertyList:extensions forType:NSFilesPromisePboardType];
292    [extensions release];
293
294    return source;
295}
296
297@end
298
299#endif // !PLATFORM(IOS)
300