Updated expected.html to reflect fix to <rdar://problem/4147745>.
[WebKit-https.git] / WebKit / WebView.subproj / WebPDFView.m
1 /*
2  * Copyright (C) 2005 Apple Computer, 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 #ifndef OMIT_TIGER_FEATURES
30
31 #import <WebKit/WebAssertions.h>
32 #import <WebKit/WebDataSource.h>
33 #import <WebKit/WebLocalizableStrings.h>
34 #import <WebKit/WebNSPasteboardExtras.h>
35 #import <WebKit/WebPDFView.h>
36
37 #import <Quartz/Quartz.h>
38
39 // QuartzPrivate.h doesn't include the PDFKit private headers, so we can't get at PDFViewPriv.h. (3957971)
40 // Even if that was fixed, we'd have to tweak compile options to include QuartzPrivate.h. (3957839)
41
42 @interface PDFDocument (PDFKitSecretsIKnow)
43 - (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate;
44 @end
45
46 NSString *_NSPathForSystemFramework(NSString *framework);
47
48 @implementation WebPDFView
49
50 + (NSBundle *)PDFKitBundle
51 {
52     static NSBundle *PDFKitBundle = nil;
53     if (PDFKitBundle == nil) {
54         NSString *PDFKitPath = [_NSPathForSystemFramework(@"Quartz.framework") stringByAppendingString:@"/Frameworks/PDFKit.framework"];
55         if (PDFKitPath == nil) {
56             ERROR("Couldn't find PDFKit.framework");
57             return nil;
58         }
59         PDFKitBundle = [NSBundle bundleWithPath:PDFKitPath];
60         if (![PDFKitBundle load]) {
61             ERROR("Couldn't load PDFKit.framework");
62         }
63     }
64     return PDFKitBundle;
65 }
66
67 + (Class)PDFViewClass
68 {
69     static Class PDFViewClass = nil;
70     if (PDFViewClass == nil) {
71         PDFViewClass = [[WebPDFView PDFKitBundle] classNamed:@"PDFView"];
72         if (PDFViewClass == nil) {
73             ERROR("Couldn't find PDFView class in PDFKit.framework");
74         }
75     }
76     return PDFViewClass;
77 }
78
79 - (id)initWithFrame:(NSRect)frame
80 {
81     self = [super initWithFrame:frame];
82     if (self) {
83         [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
84         PDFSubview = [[[[self class] PDFViewClass] alloc] initWithFrame:frame];
85         [PDFSubview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
86         [self addSubview:PDFSubview];
87         written = NO;
88     }
89     return self;
90 }
91
92 - (void)dealloc
93 {
94     [PDFSubview release];
95     [path release];
96     [super dealloc];
97 }
98
99 - (PDFView *)PDFSubview
100 {
101     return PDFSubview;
102 }
103
104 #define TEMP_PREFIX "/tmp/XXXXXX-"
105 #define OBJC_TEMP_PREFIX @"/tmp/XXXXXX-"
106
107 static void applicationInfoForMIMEType(NSString *type, NSString **name, NSImage **image)
108 {
109     NSURL *appURL = nil;
110     
111     OSStatus error = LSCopyApplicationForMIMEType((CFStringRef)type, kLSRolesAll, (CFURLRef *)&appURL);
112     if(error != noErr){
113         return;
114     }
115
116     NSString *appPath = [appURL path];
117     CFRelease (appURL);
118     
119     *image = [[NSWorkspace sharedWorkspace] iconForFile:appPath];  
120     [*image setSize:NSMakeSize(16.f,16.f)];  
121     
122     NSString *appName = [[NSFileManager defaultManager] displayNameAtPath:appPath];
123     *name = appName;
124 }
125
126
127 - (NSString *)path
128 {
129     // Generate path once.
130     if (path)
131         return path;
132         
133     NSString *filename = [[dataSource response] suggestedFilename];
134     NSFileManager *manager = [NSFileManager defaultManager];    
135     
136     path = [@"/tmp/" stringByAppendingPathComponent: filename];
137     if ([manager fileExistsAtPath:path]) {
138         path = [OBJC_TEMP_PREFIX stringByAppendingString:filename];
139         char *cpath = (char *)[path fileSystemRepresentation];
140         
141         int fd = mkstemps (cpath, strlen(cpath) - strlen(TEMP_PREFIX) + 1);
142         if (fd < 0) {
143             // Couldn't create a temporary file!  Should never happen.  Do
144             // we need an alert here?
145             path = nil;
146         }
147         else {
148             close (fd);
149             path = [manager stringWithFileSystemRepresentation:cpath length:strlen(cpath)];
150         }
151     }
152     
153     [path retain];
154     
155     return path;
156 }
157
158 - (NSView *)hitTest:(NSPoint)point
159 {
160     // Override hitTest so we can override menuForEvent.
161     NSEvent *event = [NSApp currentEvent];
162     NSEventType type = [event type];
163     if (type == NSRightMouseDown || (type == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask))) {
164         return self;
165     }
166     return [super hitTest:point];
167 }
168
169 - (NSMenu *)menuForEvent:(NSEvent *)theEvent
170 {
171     NSMenu *menu = [PDFSubview menuForEvent:theEvent];
172     
173     NSString *appName = nil;
174     NSImage *appIcon = nil;
175     
176     applicationInfoForMIMEType([[dataSource response] MIMEType], &appName, &appIcon);
177     if (!appName)
178         appName = UI_STRING("Finder", "Default application name for Open With context menu");
179     
180     NSString *title = [NSString stringWithFormat:UI_STRING("Open with %@", "Open document using the Finder"), appName];
181     
182     NSMenuItem *item = [menu insertItemWithTitle:title action:@selector(openWithFinder:) keyEquivalent:@"" atIndex:0];
183     if (appIcon)
184         [item setImage:appIcon];
185         
186     [menu insertItem:[NSMenuItem separatorItem] atIndex:1];
187     
188     return menu;
189 }
190
191 - (void)setDataSource:(WebDataSource *)ds
192 {
193     dataSource = ds;
194     [self setFrame:[[self superview] frame]];
195 }
196
197 - (void)dataSourceUpdated:(WebDataSource *)dataSource
198 {
199 }
200
201 - (void)setNeedsLayout:(BOOL)flag
202 {
203 }
204
205 - (void)layout
206 {
207 }
208
209 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
210 {
211 }
212
213 - (void)viewDidMoveToHostWindow
214 {
215 }
216
217 - (void)openWithFinder:(id)sender
218 {
219     NSString *opath = [self path];
220     
221     if (opath) {
222         if (!written) {
223             [[dataSource data] writeToFile:opath atomically:YES];
224             written = YES;
225         }
226     
227         if (![[NSWorkspace sharedWorkspace] openFile:opath]) {
228             // NSWorkspace couldn't open file.  Do we need an alert
229             // here?  We ignore the error elsewhere.
230         }
231     }
232 }
233
234 - (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag;
235 {
236     int options = 0;
237     if (!forward) {
238         options |= NSBackwardsSearch;
239     }
240     if (!caseFlag) {
241         options |= NSCaseInsensitiveSearch;
242     }
243     PDFDocument *document = [PDFSubview document];
244     PDFSelection *selection = [document findString:string fromSelection:[PDFSubview currentSelection] withOptions:options];
245     if (selection == nil && wrapFlag) {
246         selection = [document findString:string fromSelection:nil withOptions:options];
247     }
248     if (selection != nil) {
249         [PDFSubview setCurrentSelection:selection];
250         [PDFSubview scrollSelectionToVisible:nil];
251         return YES;
252     }
253     return NO;
254 }
255
256 - (void)takeFindStringFromSelection:(id)sender
257 {
258     [NSPasteboard _web_setFindPasteboardString:[[PDFSubview currentSelection] string] withOwner:self];
259 }
260
261 - (void)jumpToSelection:(id)sender
262 {
263     [PDFSubview scrollSelectionToVisible:nil];
264 }
265
266 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 
267 {
268     SEL action = [item action];    
269     if (action == @selector(takeFindStringFromSelection:) || action == @selector(jumpToSelection:)) {
270         return [PDFSubview currentSelection] != nil;
271     }
272     return YES;
273 }
274
275 - (BOOL)canPrintHeadersAndFooters
276 {
277     return NO;
278 }
279
280 - (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
281 {
282     return [[PDFSubview document] getPrintOperationForPrintInfo:printInfo autoRotate:YES];
283 }
284
285 @end
286
287 #endif // OMIT_TIGER_FEATURES