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