1d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky/*
2d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *
4d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * Redistribution and use in source and binary forms, with or without
5d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * modification, are permitted provided that the following conditions
6d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * are met:
7d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *
8d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * 1.  Redistributions of source code must retain the above copyright
9d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *     notice, this list of conditions and the following disclaimer.
10d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * 2.  Redistributions in binary form must reproduce the above copyright
11d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *     notice, this list of conditions and the following disclaimer in the
12d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *     documentation and/or other materials provided with the distribution.
13d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *     its contributors may be used to endorse or promote products derived
15d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *     from this software without specific prior written permission.
16d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky *
17d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky */
28d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
29d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebPDFView.h"
30d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
31d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "DOMNodeInternal.h"
32d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "DOMRangeInternal.h"
33d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebDataSourceInternal.h"
34d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebDelegateImplementationCaching.h"
35d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebDocumentInternal.h"
36d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebDocumentPrivate.h"
37d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebFrame.h"
38d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebFrameInternal.h"
39d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebFrameView.h"
40d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebLocalizableStringsInternal.h"
41d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebNSArrayExtras.h"
42d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebNSPasteboardExtras.h"
43d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebNSViewExtras.h"
44d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebPDFRepresentation.h"
45d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebPreferencesPrivate.h"
46d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebUIDelegate.h"
47d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebUIDelegatePrivate.h"
48d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebView.h"
49d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import "WebViewInternal.h"
50d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <PDFKit/PDFKit.h>
51d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/EventNames.h>
52d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/FormState.h>
53d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/Frame.h>
54d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/FrameLoadRequest.h>
55d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/FrameLoader.h>
56d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/HTMLFormElement.h>
57d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/HTMLFrameOwnerElement.h>
58d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/KURL.h>
59d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/KeyboardEvent.h>
60d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/MouseEvent.h>
61d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/PlatformKeyboardEvent.h>
62d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/RuntimeApplicationChecks.h>
63d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <WebCore/WebNSAttributedStringExtras.h>
64d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#import <wtf/Assertions.h>
65d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
66d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyanskyusing namespace WebCore;
67d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
68d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// Redeclarations of PDFKit notifications. We can't use the API since we use a weak link to the framework.
69d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#define _webkit_PDFViewDisplayModeChangedNotification @"PDFViewDisplayModeChanged"
70d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#define _webkit_PDFViewScaleChangedNotification @"PDFViewScaleChanged"
71d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky#define _webkit_PDFViewPageChangedNotification @"PDFViewChangedPage"
72d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
73d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky@interface PDFDocument (PDFKitSecretsIKnow)
74d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate;
75d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky@end
76d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
77d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyanskyextern "C" NSString *_NSPathForSystemFramework(NSString *framework);
78d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
79d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky@interface WebPDFView (FileInternal)
80d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky+ (Class)_PDFPreviewViewClass;
81d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky+ (Class)_PDFViewClass;
82d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)_anyPDFTagsFoundInMenu:(NSMenu *)menu;
83d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)_applyPDFDefaults;
84d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)_canLookUpInDictionary;
85d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSClipView *)_clipViewForPDFDocumentView;
86d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSEvent *)_fakeKeyEventWithFunctionKey:(unichar)functionKey;
87d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSMutableArray *)_menuItemsFromPDFKitForEvent:(NSEvent *)theEvent;
88d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection;
89d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)_openWithFinder:(id)sender;
90d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSString *)_path;
91d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)_PDFDocumentViewMightHaveScrolled:(NSNotification *)notification;
92d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)_pointIsInSelection:(NSPoint)point;
93d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSAttributedString *)_scaledAttributedString:(NSAttributedString *)unscaledAttributedString;
94d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)_setTextMatches:(NSArray *)array;
95d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSString *)_temporaryPDFDirectoryPath;
96d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)_trackFirstResponder;
97d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)_updatePreferencesSoon;
98d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSSet *)_visiblePDFPages;
99d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky@end;
100d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
101d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// PDFPrefUpdatingProxy is a class that forwards everything it gets to a target and updates the PDF viewing prefs
102d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// after each of those messages.  We use it as a way to hook all the places that the PDF viewing attrs change.
103d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky@interface PDFPrefUpdatingProxy : NSProxy {
104d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    WebPDFView *view;
105d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
106d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (id)initWithView:(WebPDFView *)view;
107d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky@end
108d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
109d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: C UTILITY FUNCTIONS
110d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
111d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyanskystatic void _applicationInfoForMIMEType(NSString *type, NSString **name, NSImage **image)
112d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
113d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSURL *appURL = nil;
114d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
115d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    OSStatus error = LSCopyApplicationForMIMEType((CFStringRef)type, kLSRolesAll, (CFURLRef *)&appURL);
116d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (error != noErr)
117d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return;
118d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
119d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSString *appPath = [appURL path];
120d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    CFRelease (appURL);
121d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
122d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    *image = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
123d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [*image setSize:NSMakeSize(16.f,16.f)];
124d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
125d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSString *appName = [[NSFileManager defaultManager] displayNameAtPath:appPath];
126d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    *name = appName;
127d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
128d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
129d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// FIXME 4182876: We can eliminate this function in favor if -isEqual: if [PDFSelection isEqual:] is overridden
130d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// to compare contents.
131d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyanskystatic BOOL _PDFSelectionsAreEqual(PDFSelection *selectionA, PDFSelection *selectionB)
132d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
133d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSArray *aPages = [selectionA pages];
134d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSArray *bPages = [selectionB pages];
135d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
136d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (![aPages isEqual:bPages])
137d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return NO;
138d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
139d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    int count = [aPages count];
140d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    int i;
141d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    for (i = 0; i < count; ++i) {
142d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSRect aBounds = [selectionA boundsForPage:[aPages objectAtIndex:i]];
143d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSRect bBounds = [selectionB boundsForPage:[bPages objectAtIndex:i]];
144d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        if (!NSEqualRects(aBounds, bBounds)) {
145d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            return NO;
146d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        }
147d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    }
148d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
149d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return YES;
150d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
151d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
152d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky@implementation WebPDFView
153d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
154d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: WebPDFView API
155d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
156d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky+ (NSBundle *)PDFKitBundle
157d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
158d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    static NSBundle *PDFKitBundle = nil;
159d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (PDFKitBundle == nil) {
160d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSString *PDFKitPath = [_NSPathForSystemFramework(@"Quartz.framework") stringByAppendingString:@"/Frameworks/PDFKit.framework"];
161d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        if (PDFKitPath == nil) {
162d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            LOG_ERROR("Couldn't find PDFKit.framework");
163d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            return nil;
164d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        }
165d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        PDFKitBundle = [NSBundle bundleWithPath:PDFKitPath];
166d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        if (![PDFKitBundle load]) {
167d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            LOG_ERROR("Couldn't load PDFKit.framework");
168d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        }
169d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    }
170d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return PDFKitBundle;
171d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
172d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
173d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky+ (NSArray *)supportedMIMETypes
174d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
175d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return [WebPDFRepresentation supportedMIMETypes];
176d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
177d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
178d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)setPDFDocument:(PDFDocument *)doc
179d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
180d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // Both setDocument: and _applyPDFDefaults will trigger scale and mode-changed notifications.
181d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // Those aren't reflecting user actions, so we need to ignore them.
182d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    _ignoreScaleAndDisplayModeAndPageNotifications = YES;
183d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview setDocument:doc];
184d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [self _applyPDFDefaults];
185d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    _ignoreScaleAndDisplayModeAndPageNotifications = NO;
186d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
187d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
188d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (PDFDocument *)PDFDocument
189d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
190d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return [PDFSubview document];
191d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
192d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
193d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: NSObject OVERRIDES
194d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
195d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)dealloc
196d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
197d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [dataSource release];
198d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [previewView release];
199d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview setDelegate:nil];
200d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview release];
201d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [path release];
202d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubviewProxy release];
203d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [textMatches release];
204d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [super dealloc];
205d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
206d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
207d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: NSResponder OVERRIDES
208d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
209d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)centerSelectionInVisibleArea:(id)sender
210d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
211d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview scrollSelectionToVisible:nil];
212d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
213d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
214d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)scrollPageDown:(id)sender
215d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
216d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // PDFView doesn't support this responder method directly, so we pass it a fake key event
217d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSPageDownFunctionKey]];
218d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
219d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
220d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)scrollPageUp:(id)sender
221d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
222d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // PDFView doesn't support this responder method directly, so we pass it a fake key event
223d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSPageUpFunctionKey]];
224d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
225d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
226d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)scrollLineDown:(id)sender
227d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
228d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // PDFView doesn't support this responder method directly, so we pass it a fake key event
229d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSDownArrowFunctionKey]];
230d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
231d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
232d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)scrollLineUp:(id)sender
233d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
234d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // PDFView doesn't support this responder method directly, so we pass it a fake key event
235d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSUpArrowFunctionKey]];
236d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
237d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
238d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)scrollToBeginningOfDocument:(id)sender
239d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
240d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // PDFView doesn't support this responder method directly, so we pass it a fake key event
241d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSHomeFunctionKey]];
242d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
243d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
244d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)scrollToEndOfDocument:(id)sender
245d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
246d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // PDFView doesn't support this responder method directly, so we pass it a fake key event
247d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSEndFunctionKey]];
248d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
249d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
250d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari
251d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// was using the old jumpToSelection selector in its menu. Newer versions of Safari will us the
252d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// selector centerSelectionInVisibleArea. We'll leave this old selector in place for two reasons:
253d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// (1) compatibility between older Safari and newer WebKit; (2) other WebKit-based applications
254d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// might be using the jumpToSelection: selector, and we don't want to break them.
255d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)jumpToSelection:(id)sender
256d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
257d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [self centerSelectionInVisibleArea:nil];
258d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
259d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
260d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: NSView OVERRIDES
261d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
262d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)acceptsFirstResponder {
263d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return YES;
264d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
265d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
266d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)becomeFirstResponder
267d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
268d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // This works together with setNextKeyView to splice our PDFSubview into
269d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // the key loop similar to the way NSScrollView does this.
270d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSWindow *window = [self window];
271d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    id newFirstResponder = nil;
272d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
273d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
274d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSView *previousValidKeyView = [self previousValidKeyView];
275d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        if ((previousValidKeyView != self) && (previousValidKeyView != PDFSubview))
276d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            newFirstResponder = previousValidKeyView;
277d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    } else {
278d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSView *PDFDocumentView = [PDFSubview documentView];
279d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        if ([PDFDocumentView acceptsFirstResponder])
280d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            newFirstResponder = PDFDocumentView;
281d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    }
282d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
283d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (!newFirstResponder)
284d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return NO;
285d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
286d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (![window makeFirstResponder:newFirstResponder])
287d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return NO;
288d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
289d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [[dataSource webFrame] _clearSelectionInOtherFrames];
290d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
291d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return YES;
292d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
293d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
294d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSView *)hitTest:(NSPoint)point
295d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
296d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // Override hitTest so we can override menuForEvent.
297d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSEvent *event = [NSApp currentEvent];
298d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSEventType type = [event type];
299d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (type == NSRightMouseDown || (type == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask)))
300d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return self;
301d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
302d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return [super hitTest:point];
303d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
304d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
305d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (id)initWithFrame:(NSRect)frame
306d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
307d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    self = [super initWithFrame:frame];
308d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (self) {
309d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
310d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
311d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        Class previewViewClass = [[self class] _PDFPreviewViewClass];
312d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
313d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // We might not have found a previewViewClass, but if we did find it
314d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // then we should be able to create an instance.
315d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        if (previewViewClass) {
316d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            previewView = [[previewViewClass alloc] initWithFrame:frame];
317d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            ASSERT(previewView);
318d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        }
319d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
320d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSView *topLevelPDFKitView = nil;
321d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        if (previewView) {
322d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            // We'll retain the PDFSubview here so that it is equally retained in all
323d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            // code paths. That way we don't need to worry about conditionally releasing
324d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            // it later.
325d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            PDFSubview = [[previewView performSelector:@selector(pdfView)] retain];
326d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            topLevelPDFKitView = previewView;
327d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        } else {
328d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            PDFSubview = [[[[self class] _PDFViewClass] alloc] initWithFrame:frame];
329d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            topLevelPDFKitView = PDFSubview;
330d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        }
331d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
33265eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky        ASSERT(PDFSubview);
333d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
334d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [topLevelPDFKitView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
335d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [self addSubview:topLevelPDFKitView];
336d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
337d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [PDFSubview setDelegate:self];
338d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        written = NO;
339d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // Messaging this proxy is the same as messaging PDFSubview, with the side effect that the
340d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // PDF viewing defaults are updated afterwards
341d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        PDFSubviewProxy = (PDFView *)[[PDFPrefUpdatingProxy alloc] initWithView:self];
342d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    }
343d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
344d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return self;
345d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
346d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
347d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSMenu *)menuForEvent:(NSEvent *)theEvent
348d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
349d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // Start with the menu items supplied by PDFKit, with WebKit tags applied
350d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSMutableArray *items = [self _menuItemsFromPDFKitForEvent:theEvent];
351d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
352d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // Add in an "Open with <default PDF viewer>" item
353d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSString *appName = nil;
354d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSImage *appIcon = nil;
355d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
356d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    _applicationInfoForMIMEType([dataSource _responseMIMEType], &appName, &appIcon);
357d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (!appName)
358d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        appName = UI_STRING_INTERNAL("Finder", "Default application name for Open With context menu");
359d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
36065eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky    // To match the PDFKit style, we'll add Open with Preview even when there's no document yet to view, and
361d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // disable it using validateUserInterfaceItem.
362d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSString *title = [NSString stringWithFormat:UI_STRING_INTERNAL("Open with %@", "context menu item for PDF"), appName];
363d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title action:@selector(_openWithFinder:) keyEquivalent:@""];
364d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [item setTag:WebMenuItemTagOpenWithDefaultApplication];
365d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (appIcon)
366d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [item setImage:appIcon];
367d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [items insertObject:item atIndex:0];
368d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [item release];
369d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
370d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [items insertObject:[NSMenuItem separatorItem] atIndex:1];
371d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
372d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // pass the items off to the WebKit context menu mechanism
373d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    WebView *webView = [[dataSource webFrame] webView];
374d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    ASSERT(webView);
375d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSMenu *menu = [webView _menuForElement:[self elementAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]] defaultItems:items];
376d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
377d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // The delegate has now had the opportunity to add items to the standard PDF-related items, or to
378d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // remove or modify some of the PDF-related items. In 10.4, the PDF context menu did not go through
379d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // the standard WebKit delegate path, and so the standard PDF-related items always appeared. For
380d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // clients that create their own context menu by hand-picking specific items from the default list, such as
381d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // Safari, none of the PDF-related items will appear until the client is rewritten to explicitly
382d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // include these items. For backwards compatibility of tip-of-tree WebKit with the 10.4 version of Safari
383d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // (the configuration that people building open source WebKit use), we'll use the entire set of PDFKit-supplied
384d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // menu items. This backward-compatibility hack won't work with any non-Safari clients, but this seems OK since
385d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // (1) the symptom is fairly minor, and (2) we suspect that non-Safari clients are probably using the entire
386d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // set of default items, rather than manually choosing from them. We can remove this code entirely when we
387d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // ship a version of Safari that includes the fix for radar 3796579.
388d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (![self _anyPDFTagsFoundInMenu:menu] && applicationIsSafari()) {
389d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [menu addItem:[NSMenuItem separatorItem]];
390d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSEnumerator *e = [items objectEnumerator];
391d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        NSMenuItem *menuItem;
392d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        while ((menuItem = [e nextObject]) != nil) {
393d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            // copy menuItem since a given menuItem can be in only one menu at a time, and we don't
394d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            // want to mess with the menu returned from PDFKit.
395d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky            [menu addItem:[menuItem copy]];
396d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        }
397d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    }
398d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
399d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return menu;
400d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
401d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
402d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)setNextKeyView:(NSView *)aView
403d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
404d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // This works together with becomeFirstResponder to splice PDFSubview into
40565eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky    // the key loop similar to the way NSScrollView and NSClipView do this.
406d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSView *documentView = [PDFSubview documentView];
407d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (documentView) {
408d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [documentView setNextKeyView:aView];
40965eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky
410d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // We need to make the documentView be the next view in the keyview loop.
411d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // It would seem more sensible to do this in our init method, but it turns out
412d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // that [NSClipView setDocumentView] won't call this method if our next key view
413d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // is already set, so we wait until we're called before adding this connection.
414d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // We'll also clear it when we're called with nil, so this could go through the
415d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        // same code path more than once successfully.
416d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [super setNextKeyView: aView ? documentView : nil];
417d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    } else
418d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [super setNextKeyView:aView];
419d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
420d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
421d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)viewDidMoveToWindow
422d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
423d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // FIXME 2573089: we can observe a notification for first responder changes
424d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // instead of the very frequent NSWindowDidUpdateNotification if/when 2573089 is addressed.
425d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSWindow *newWindow = [self window];
426d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (!newWindow)
427d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return;
428d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
429d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [self _trackFirstResponder];
430d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
431d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter addObserver:self
432d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                           selector:@selector(_trackFirstResponder)
43365eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky                               name:NSWindowDidUpdateNotification
434d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                             object:newWindow];
435d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
436d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter addObserver:self
437d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                           selector:@selector(_scaleOrDisplayModeOrPageChanged:)
438d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                               name:_webkit_PDFViewScaleChangedNotification
439d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                             object:PDFSubview];
440d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
441d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter addObserver:self
442d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                           selector:@selector(_scaleOrDisplayModeOrPageChanged:)
443d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                               name:_webkit_PDFViewDisplayModeChangedNotification
444d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                             object:PDFSubview];
445d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
446d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter addObserver:self
447d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                           selector:@selector(_scaleOrDisplayModeOrPageChanged:)
44865eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky                               name:_webkit_PDFViewPageChangedNotification
449d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                             object:PDFSubview];
450d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
451d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter addObserver:self
452d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                           selector:@selector(_PDFDocumentViewMightHaveScrolled:)
453d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                               name:NSViewBoundsDidChangeNotification
454d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                             object:[self _clipViewForPDFDocumentView]];
455d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
456d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
457d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)viewWillMoveToWindow:(NSWindow *)window
458d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
459d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // FIXME 2573089: we can observe a notification for changes to the first responder
460d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // instead of the very frequent NSWindowDidUpdateNotification if/when 2573089 is addressed.
461d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSWindow *oldWindow = [self window];
462d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (!oldWindow)
463d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return;
464d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
465d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
46665eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky    [notificationCenter removeObserver:self
467d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                  name:NSWindowDidUpdateNotification
468d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                object:oldWindow];
469d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter removeObserver:self
470d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                  name:_webkit_PDFViewScaleChangedNotification
471d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                object:PDFSubview];
472d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter removeObserver:self
473d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                  name:_webkit_PDFViewDisplayModeChangedNotification
474d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                object:PDFSubview];
475d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter removeObserver:self
476d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                  name:_webkit_PDFViewPageChangedNotification
477d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                object:PDFSubview];
47865eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky
479d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [notificationCenter removeObserver:self
480d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                  name:NSViewBoundsDidChangeNotification
481d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky                                object:[self _clipViewForPDFDocumentView]];
482d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
483d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    firstResponderIsPDFDocumentView = NO;
484d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
485d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
486d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: NSUserInterfaceValidations PROTOCOL IMPLEMENTATION
487d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
488d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
489d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
490d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    SEL action = [item action];
491d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (action == @selector(takeFindStringFromSelection:) || action == @selector(centerSelectionInVisibleArea:) || action == @selector(jumpToSelection:))
492d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return [PDFSubview currentSelection] != nil;
493d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
494d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (action == @selector(_openWithFinder:))
495d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return [PDFSubview document] != nil;
49665eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky
497d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (action == @selector(_lookUpInDictionaryFromMenu:))
498d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return [self _canLookUpInDictionary];
499d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
50065eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky    return YES;
501d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
502d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
503d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
504d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
505d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean
506d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // assumes the WebVIew is non-nil.
507d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (![self _webView])
508d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return NO;
509d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
510d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
511d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
51265eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky
513d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: INTERFACE BUILDER ACTIONS FOR SAFARI
514d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
515d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// Surprisingly enough, this isn't defined in any superclass, though it is defined in assorted AppKit classes since
516d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// it's a standard menu item IBAction.
517d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (IBAction)copy:(id)sender
518d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
519d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [PDFSubview copy:sender];
520d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
521d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
522d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// This used to be a standard IBAction (for Use Selection For Find), but AppKit now uses performFindPanelAction:
523d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// with a menu item tag for this purpose.
524d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (IBAction)takeFindStringFromSelection:(id)sender
525d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
526d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [NSPasteboard _web_setFindPasteboardString:[[PDFSubview currentSelection] string] withOwner:self];
52765eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky}
528d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
529d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: WebFrameView UNDECLARED "DELEGATE METHODS"
530d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
531d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// This is tested in -[WebFrameView canPrintHeadersAndFooters], but isn't declared anywhere (yuck)
532d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (BOOL)canPrintHeadersAndFooters
533d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
534d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return NO;
535d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
536d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
537d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// This is tested in -[WebFrameView printOperationWithPrintInfo:], but isn't declared anywhere (yuck)
538d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
53965eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky{
540d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return [[PDFSubview document] getPrintOperationForPrintInfo:printInfo autoRotate:YES];
541d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
542d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
543d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: WebDocumentView PROTOCOL IMPLEMENTATION
544d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
545d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)setDataSource:(WebDataSource *)ds
546d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
547d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    if (dataSource == ds)
548d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        return;
549d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
550d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    dataSource = [ds retain];
551d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
552d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // FIXME: There must be some better place to put this. There is no comment in ChangeLog
553d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    // explaining why it's in this method.
554d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    [self setFrame:[[self superview] frame]];
55565eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky}
556d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
557d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)dataSourceUpdated:(WebDataSource *)dataSource
558d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
559d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
560d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
561d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)setNeedsLayout:(BOOL)flag
562d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
563d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
564d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
565d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)layout
566d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
567d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
568d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
569d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
570d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
571d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
572d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
573d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (void)viewDidMoveToHostWindow
574d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
575d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
576d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
577d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky// MARK: WebDocumentElement PROTOCOL IMPLEMENTATION
578d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
579d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSDictionary *)elementAtPoint:(NSPoint)point
580d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
581d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    WebFrame *frame = [dataSource webFrame];
582d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    ASSERT(frame);
583d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
584d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return [NSDictionary dictionaryWithObjectsAndKeys:
585d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        frame, WebElementFrameKey,
586d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        [NSNumber numberWithBool:[self _pointIsInSelection:point]], WebElementIsSelectedKey,
587d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky        nil];
588d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
58965eccecf5c288b7b1bc8bf985fa59d3dd4ce0cfbMax Krasnyansky
590d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky- (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow
591d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky{
592d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky    return [self elementAtPoint:point];
593d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky}
594d2214ceffa8f235a26ff0c6ab4b17f0a05f5d455Max Krasnyansky
595// MARK: WebDocumentSearching PROTOCOL IMPLEMENTATION
596
597- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
598{
599    return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
600}
601
602// MARK: WebDocumentIncrementalSearching PROTOCOL IMPLEMENTATION
603
604- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
605{
606    PDFSelection *selection = [self _nextMatchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag fromSelection:[PDFSubview currentSelection] startInSelection:startInSelection];
607    if (!selection)
608        return NO;
609
610    [PDFSubview setCurrentSelection:selection];
611    [PDFSubview scrollSelectionToVisible:nil];
612    return YES;
613}
614
615// MARK: WebMultipleTextMatches PROTOCOL IMPLEMENTATION
616
617- (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue
618{
619    // This method is part of the WebMultipleTextMatches algorithm, but this class doesn't support
620    // highlighting text matches inline.
621#ifndef NDEBUG
622    if (newValue)
623        LOG_ERROR("[WebPDFView setMarkedTextMatchesAreHighlighted:] called with YES, which isn't supported");
624#endif
625}
626
627- (BOOL)markedTextMatchesAreHighlighted
628{
629    return NO;
630}
631
632static BOOL isFrameInRange(WebFrame *frame, DOMRange *range)
633{
634    BOOL inRange = NO;
635    for (HTMLFrameOwnerElement* ownerElement = core(frame)->ownerElement(); ownerElement; ownerElement = ownerElement->document()->frame()->ownerElement()) {
636        if (ownerElement->document() == core(range)->ownerDocument()) {
637            inRange = [range intersectsNode:kit(ownerElement)];
638            break;
639        }
640    }
641    return inRange;
642}
643
644- (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches
645{
646    if (range && !isFrameInRange([dataSource webFrame], range))
647        return 0;
648
649    PDFSelection *previousMatch = nil;
650    NSMutableArray *matches = [[NSMutableArray alloc] initWithCapacity:limit];
651
652    for (;;) {
653        PDFSelection *nextMatch = [self _nextMatchFor:string direction:YES caseSensitive:!(options & WebFindOptionsCaseInsensitive) wrap:NO fromSelection:previousMatch startInSelection:NO];
654        if (!nextMatch)
655            break;
656
657        [matches addObject:nextMatch];
658        previousMatch = nextMatch;
659
660        if ([matches count] >= limit)
661            break;
662    }
663
664    [self _setTextMatches:matches];
665    [matches release];
666
667    return [matches count];
668}
669
670- (void)unmarkAllTextMatches
671{
672    [self _setTextMatches:nil];
673}
674
675- (NSArray *)rectsForTextMatches
676{
677    NSMutableArray *result = [NSMutableArray arrayWithCapacity:[textMatches count]];
678    NSSet *visiblePages = [self _visiblePDFPages];
679    NSEnumerator *matchEnumerator = [textMatches objectEnumerator];
680    PDFSelection *match;
681
682    while ((match = [matchEnumerator nextObject]) != nil) {
683        NSEnumerator *pages = [[match pages] objectEnumerator];
684        PDFPage *page;
685        while ((page = [pages nextObject]) != nil) {
686
687            // Skip pages that aren't visible (needed for non-continuous modes, see 5362989)
688            if (![visiblePages containsObject:page])
689                continue;
690
691            NSRect selectionOnPageInPDFViewCoordinates = [PDFSubview convertRect:[match boundsForPage:page] fromPage:page];
692            [result addObject:[NSValue valueWithRect:selectionOnPageInPDFViewCoordinates]];
693        }
694    }
695
696    return result;
697}
698
699// MARK: WebDocumentText PROTOCOL IMPLEMENTATION
700
701- (BOOL)supportsTextEncoding
702{
703    return NO;
704}
705
706- (NSString *)string
707{
708    return [[PDFSubview document] string];
709}
710
711- (NSAttributedString *)attributedString
712{
713    // changing the selection is a hack, but the only way to get an attr string is via PDFSelection
714
715    // must copy this selection object because we change the selection which seems to release it
716    PDFSelection *savedSelection = [[PDFSubview currentSelection] copy];
717    [PDFSubview selectAll:nil];
718    NSAttributedString *result = [[PDFSubview currentSelection] attributedString];
719    if (savedSelection) {
720        [PDFSubview setCurrentSelection:savedSelection];
721        [savedSelection release];
722    } else {
723        // FIXME: behavior of setCurrentSelection:nil is not documented - check 4182934 for progress
724        // Otherwise, we could collapse this code with the case above.
725        [PDFSubview clearSelection];
726    }
727
728    result = [self _scaledAttributedString:result];
729
730    return result;
731}
732
733- (NSString *)selectedString
734{
735    return [[PDFSubview currentSelection] string];
736}
737
738- (NSAttributedString *)selectedAttributedString
739{
740    return [self _scaledAttributedString:[[PDFSubview currentSelection] attributedString]];
741}
742
743- (void)selectAll
744{
745    [PDFSubview selectAll:nil];
746}
747
748- (void)deselectAll
749{
750    [PDFSubview clearSelection];
751}
752
753// MARK: WebDocumentViewState PROTOCOL IMPLEMENTATION
754
755// Even though to WebKit we are the "docView", in reality a PDFView contains its own scrollview and docView.
756// And it even turns out there is another PDFKit view between the docView and its enclosing ScrollView, so
757// we have to be sure to do our calculations based on that view, immediately inside the ClipView.  We try
758// to make as few assumptions about the PDFKit view hierarchy as possible.
759
760- (NSPoint)scrollPoint
761{
762    NSView *realDocView = [PDFSubview documentView];
763    NSClipView *clipView = [[realDocView enclosingScrollView] contentView];
764    return [clipView bounds].origin;
765}
766
767- (void)setScrollPoint:(NSPoint)p
768{
769    WebFrame *frame = [dataSource webFrame];
770    //FIXME:  We only restore scroll state in the non-frames case because otherwise we get a crash due to
771    // PDFKit calling display from within its drawRect:. See bugzilla 4164.
772    if (![frame parentFrame]) {
773        NSView *realDocView = [PDFSubview documentView];
774        [[[realDocView enclosingScrollView] documentView] scrollPoint:p];
775    }
776}
777
778- (id)viewState
779{
780    NSMutableArray *state = [NSMutableArray arrayWithCapacity:4];
781    PDFDisplayMode mode = [PDFSubview displayMode];
782    [state addObject:[NSNumber numberWithInt:mode]];
783    if (mode == kPDFDisplaySinglePage || mode == kPDFDisplayTwoUp) {
784        unsigned int pageIndex = [[PDFSubview document] indexForPage:[PDFSubview currentPage]];
785        [state addObject:[NSNumber numberWithUnsignedInt:pageIndex]];
786    }  // else in continuous modes, scroll position gets us to the right page
787    BOOL autoScaleFlag = [PDFSubview autoScales];
788    [state addObject:[NSNumber numberWithBool:autoScaleFlag]];
789    if (!autoScaleFlag)
790        [state addObject:[NSNumber numberWithFloat:[PDFSubview scaleFactor]]];
791
792    return state;
793}
794
795- (void)setViewState:(id)statePList
796{
797    ASSERT([statePList isKindOfClass:[NSArray class]]);
798    NSArray *state = statePList;
799    int i = 0;
800    PDFDisplayMode mode = [[state objectAtIndex:i++] intValue];
801    [PDFSubview setDisplayMode:mode];
802    if (mode == kPDFDisplaySinglePage || mode == kPDFDisplayTwoUp) {
803        unsigned int pageIndex = [[state objectAtIndex:i++] unsignedIntValue];
804        [PDFSubview goToPage:[[PDFSubview document] pageAtIndex:pageIndex]];
805    }  // else in continuous modes, scroll position gets us to the right page
806    BOOL autoScaleFlag = [[state objectAtIndex:i++] boolValue];
807    [PDFSubview setAutoScales:autoScaleFlag];
808    if (!autoScaleFlag)
809        [PDFSubview setScaleFactor:[[state objectAtIndex:i++] floatValue]];
810}
811
812// MARK: _WebDocumentTextSizing PROTOCOL IMPLEMENTATION
813
814- (IBAction)_zoomOut:(id)sender
815{
816    [PDFSubviewProxy zoomOut:sender];
817}
818
819- (IBAction)_zoomIn:(id)sender
820{
821    [PDFSubviewProxy zoomIn:sender];
822}
823
824- (IBAction)_resetZoom:(id)sender
825{
826    [PDFSubviewProxy setScaleFactor:1.0f];
827}
828
829- (BOOL)_canZoomOut
830{
831    return [PDFSubview canZoomOut];
832}
833
834- (BOOL)_canZoomIn
835{
836    return [PDFSubview canZoomIn];
837}
838
839- (BOOL)_canResetZoom
840{
841    return [PDFSubview scaleFactor] != 1.0;
842}
843
844// MARK: WebDocumentSelection PROTOCOL IMPLEMENTATION
845
846- (NSRect)selectionRect
847{
848    NSRect result = NSZeroRect;
849    PDFSelection *selection = [PDFSubview currentSelection];
850    NSEnumerator *pages = [[selection pages] objectEnumerator];
851    PDFPage *page;
852    while ((page = [pages nextObject]) != nil) {
853        NSRect selectionOnPageInPDFViewCoordinates = [PDFSubview convertRect:[selection boundsForPage:page] fromPage:page];
854        if (NSIsEmptyRect(result))
855            result = selectionOnPageInPDFViewCoordinates;
856        else
857            result = NSUnionRect(result, selectionOnPageInPDFViewCoordinates);
858    }
859
860    // Convert result to be in documentView (selectionView) coordinates
861    result = [PDFSubview convertRect:result toView:[PDFSubview documentView]];
862
863    return result;
864}
865
866- (NSArray *)selectionTextRects
867{
868    // FIXME: We'd need new PDFKit API/SPI to get multiple text rects for selections that intersect more than one line
869    return [NSArray arrayWithObject:[NSValue valueWithRect:[self selectionRect]]];
870}
871
872- (NSView *)selectionView
873{
874    return [PDFSubview documentView];
875}
876
877- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
878{
879    // Convert the selection to an attributed string, and draw that.
880    // FIXME 4621154: this doesn't handle italics (and maybe other styles)
881    // FIXME 4604366: this doesn't handle text at non-actual size
882    NSMutableAttributedString *attributedString = [[self selectedAttributedString] mutableCopy];
883    NSRange wholeStringRange = NSMakeRange(0, [attributedString length]);
884
885    // Modify the styles in the attributed string to draw black text, no background, and no underline. We draw
886    // no underline because it would look ugly.
887    [attributedString beginEditing];
888    [attributedString removeAttribute:NSBackgroundColorAttributeName range:wholeStringRange];
889    [attributedString removeAttribute:NSUnderlineStyleAttributeName range:wholeStringRange];
890    if (forceBlackText)
891        [attributedString addAttribute:NSForegroundColorAttributeName value:[NSColor colorWithDeviceWhite:0.0f alpha:1.0f] range:wholeStringRange];
892    [attributedString endEditing];
893
894    NSImage* selectionImage = [[[NSImage alloc] initWithSize:[self selectionRect].size] autorelease];
895
896    [selectionImage lockFocus];
897    [attributedString drawAtPoint:NSZeroPoint];
898    [selectionImage unlockFocus];
899
900    [attributedString release];
901
902    return selectionImage;
903}
904
905- (NSRect)selectionImageRect
906{
907    // FIXME: deal with clipping?
908    return [self selectionRect];
909}
910
911- (NSArray *)pasteboardTypesForSelection
912{
913    return [NSArray arrayWithObjects:NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil];
914}
915
916- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
917{
918    NSAttributedString *attributedString = [self selectedAttributedString];
919
920    if ([types containsObject:NSRTFDPboardType]) {
921        NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
922        [pasteboard setData:RTFDData forType:NSRTFDPboardType];
923    }
924
925    if ([types containsObject:NSRTFPboardType]) {
926        if ([attributedString containsAttachments])
927            attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
928
929        NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil];
930        [pasteboard setData:RTFData forType:NSRTFPboardType];
931    }
932
933    if ([types containsObject:NSStringPboardType])
934        [pasteboard setString:[self selectedString] forType:NSStringPboardType];
935}
936
937// MARK: PDFView DELEGATE METHODS
938
939- (void)PDFViewWillClickOnLink:(PDFView *)sender withURL:(NSURL *)URL
940{
941    if (!URL)
942        return;
943
944    NSWindow *window = [sender window];
945    NSEvent *nsEvent = [window currentEvent];
946    const int noButton = -1;
947    int button = noButton;
948    RefPtr<Event> event;
949    switch ([nsEvent type]) {
950        case NSLeftMouseUp:
951            button = 0;
952            break;
953        case NSRightMouseUp:
954            button = 1;
955            break;
956        case NSOtherMouseUp:
957            button = [nsEvent buttonNumber];
958            break;
959        case NSKeyDown: {
960            PlatformKeyboardEvent pe(nsEvent);
961            pe.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown);
962            event = KeyboardEvent::create(eventNames().keydownEvent, true, true, 0,
963                pe.keyIdentifier(), pe.windowsVirtualKeyCode(),
964                pe.ctrlKey(), pe.altKey(), pe.shiftKey(), pe.metaKey(), false);
965        }
966        default:
967            break;
968    }
969    if (button != noButton) {
970        event = MouseEvent::create(eventNames().clickEvent, true, true, 0, [nsEvent clickCount], 0, 0, 0, 0,
971            [nsEvent modifierFlags] & NSControlKeyMask,
972            [nsEvent modifierFlags] & NSAlternateKeyMask,
973            [nsEvent modifierFlags] & NSShiftKeyMask,
974            [nsEvent modifierFlags] & NSCommandKeyMask,
975            button, 0, 0, true);
976    }
977
978    // Call to the frame loader because this is where our security checks are made.
979    Frame* frame = core([dataSource webFrame]);
980    frame->loader()->loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(URL)), false, false, event.get(), 0, SendReferrer);
981}
982
983- (void)PDFViewOpenPDFInNativeApplication:(PDFView *)sender
984{
985    // Delegate method sent when the user requests opening the PDF file in the system's default app
986    [self _openWithFinder:sender];
987}
988
989- (void)PDFViewPerformPrint:(PDFView *)sender
990{
991    CallUIDelegate([self _webView], @selector(webView:printFrameView:), [[dataSource webFrame] frameView]);
992}
993
994- (void)PDFViewSavePDFToDownloadFolder:(PDFView *)sender
995{
996    // We don't want to write the file until we have a document to write (see 5267607).
997    if (![PDFSubview document]) {
998        NSBeep();
999        return;
1000    }
1001
1002    // Delegate method sent when the user requests downloading the PDF file to disk. We pass NO for
1003    // showingPanel: so that the PDF file is saved to the standard location without user intervention.
1004    CallUIDelegate([self _webView], @selector(webView:saveFrameView:showingPanel:), [[dataSource webFrame] frameView], NO);
1005}
1006
1007@end
1008
1009@implementation WebPDFView (FileInternal)
1010
1011+ (Class)_PDFPreviewViewClass
1012{
1013    static Class PDFPreviewViewClass = nil;
1014    static BOOL checkedForPDFPreviewViewClass = NO;
1015
1016    if (!checkedForPDFPreviewViewClass) {
1017        checkedForPDFPreviewViewClass = YES;
1018        PDFPreviewViewClass = [[WebPDFView PDFKitBundle] classNamed:@"PDFPreviewView"];
1019    }
1020
1021    // This class might not be available; callers need to deal with a nil return here.
1022    return PDFPreviewViewClass;
1023}
1024
1025+ (Class)_PDFViewClass
1026{
1027    static Class PDFViewClass = nil;
1028    if (PDFViewClass == nil) {
1029        PDFViewClass = [[WebPDFView PDFKitBundle] classNamed:@"PDFView"];
1030        if (!PDFViewClass)
1031            LOG_ERROR("Couldn't find PDFView class in PDFKit.framework");
1032    }
1033    return PDFViewClass;
1034}
1035
1036- (BOOL)_anyPDFTagsFoundInMenu:(NSMenu *)menu
1037{
1038    NSEnumerator *e = [[menu itemArray] objectEnumerator];
1039    NSMenuItem *item;
1040    while ((item = [e nextObject]) != nil) {
1041        switch ([item tag]) {
1042            case WebMenuItemTagOpenWithDefaultApplication:
1043            case WebMenuItemPDFActualSize:
1044            case WebMenuItemPDFZoomIn:
1045            case WebMenuItemPDFZoomOut:
1046            case WebMenuItemPDFAutoSize:
1047            case WebMenuItemPDFSinglePage:
1048            case WebMenuItemPDFSinglePageScrolling:
1049            case WebMenuItemPDFFacingPages:
1050            case WebMenuItemPDFFacingPagesScrolling:
1051            case WebMenuItemPDFContinuous:
1052            case WebMenuItemPDFNextPage:
1053            case WebMenuItemPDFPreviousPage:
1054                return YES;
1055        }
1056    }
1057    return NO;
1058}
1059
1060- (void)_applyPDFDefaults
1061{
1062    // Set up default viewing params
1063    WebPreferences *prefs = [[dataSource _webView] preferences];
1064    float scaleFactor = [prefs PDFScaleFactor];
1065    if (scaleFactor == 0)
1066        [PDFSubview setAutoScales:YES];
1067    else {
1068        [PDFSubview setAutoScales:NO];
1069        [PDFSubview setScaleFactor:scaleFactor];
1070    }
1071    [PDFSubview setDisplayMode:[prefs PDFDisplayMode]];
1072}
1073
1074- (BOOL)_canLookUpInDictionary
1075{
1076    return [PDFSubview respondsToSelector:@selector(_searchInDictionary:)];
1077}
1078
1079- (NSClipView *)_clipViewForPDFDocumentView
1080{
1081    NSClipView *clipView = (NSClipView *)[[PDFSubview documentView] _web_superviewOfClass:[NSClipView class]];
1082    ASSERT(clipView);
1083    return clipView;
1084}
1085
1086- (NSEvent *)_fakeKeyEventWithFunctionKey:(unichar)functionKey
1087{
1088    // FIXME 4400480: when PDFView implements the standard scrolling selectors that this
1089    // method is used to mimic, we can eliminate this method and call them directly.
1090    NSString *keyAsString = [NSString stringWithCharacters:&functionKey length:1];
1091    return [NSEvent keyEventWithType:NSKeyDown
1092                            location:NSZeroPoint
1093                       modifierFlags:0
1094                           timestamp:0
1095                        windowNumber:0
1096                             context:nil
1097                          characters:keyAsString
1098         charactersIgnoringModifiers:keyAsString
1099                           isARepeat:NO
1100                             keyCode:0];
1101}
1102
1103- (void)_lookUpInDictionaryFromMenu:(id)sender
1104{
1105    // This method is used by WebKit's context menu item. Here we map to the method that
1106    // PDFView uses. Since the PDFView method isn't API, and isn't available on all versions
1107    // of PDFKit, we use performSelector after a respondsToSelector check, rather than calling it directly.
1108    if ([self _canLookUpInDictionary])
1109        [PDFSubview performSelector:@selector(_searchInDictionary:) withObject:sender];
1110}
1111
1112- (NSMutableArray *)_menuItemsFromPDFKitForEvent:(NSEvent *)theEvent
1113{
1114    NSMutableArray *copiedItems = [NSMutableArray array];
1115    NSDictionary *actionsToTags = [[NSDictionary alloc] initWithObjectsAndKeys:
1116        [NSNumber numberWithInt:WebMenuItemPDFActualSize], NSStringFromSelector(@selector(_setActualSize:)),
1117        [NSNumber numberWithInt:WebMenuItemPDFZoomIn], NSStringFromSelector(@selector(zoomIn:)),
1118        [NSNumber numberWithInt:WebMenuItemPDFZoomOut], NSStringFromSelector(@selector(zoomOut:)),
1119        [NSNumber numberWithInt:WebMenuItemPDFAutoSize], NSStringFromSelector(@selector(_setAutoSize:)),
1120        [NSNumber numberWithInt:WebMenuItemPDFSinglePage], NSStringFromSelector(@selector(_setSinglePage:)),
1121        [NSNumber numberWithInt:WebMenuItemPDFSinglePageScrolling], NSStringFromSelector(@selector(_setSinglePageScrolling:)),
1122        [NSNumber numberWithInt:WebMenuItemPDFFacingPages], NSStringFromSelector(@selector(_setDoublePage:)),
1123        [NSNumber numberWithInt:WebMenuItemPDFFacingPagesScrolling], NSStringFromSelector(@selector(_setDoublePageScrolling:)),
1124        [NSNumber numberWithInt:WebMenuItemPDFContinuous], NSStringFromSelector(@selector(_toggleContinuous:)),
1125        [NSNumber numberWithInt:WebMenuItemPDFNextPage], NSStringFromSelector(@selector(goToNextPage:)),
1126        [NSNumber numberWithInt:WebMenuItemPDFPreviousPage], NSStringFromSelector(@selector(goToPreviousPage:)),
1127        nil];
1128
1129    // Leave these menu items out, since WebKit inserts equivalent ones. Note that we leave out PDFKit's "Look Up in Dictionary"
1130    // item here because WebKit already includes an item with the same title and purpose. We map WebKit's to PDFKit's
1131    // "Look Up in Dictionary" via the implementation of -[WebPDFView _lookUpInDictionaryFromMenu:].
1132    NSSet *unwantedActions = [[NSSet alloc] initWithObjects:
1133                              NSStringFromSelector(@selector(_searchInSpotlight:)),
1134                              NSStringFromSelector(@selector(_searchInGoogle:)),
1135                              NSStringFromSelector(@selector(_searchInDictionary:)),
1136                              NSStringFromSelector(@selector(copy:)),
1137                              nil];
1138
1139    NSEnumerator *e = [[[PDFSubview menuForEvent:theEvent] itemArray] objectEnumerator];
1140    NSMenuItem *item;
1141    while ((item = [e nextObject]) != nil) {
1142
1143        NSString *actionString = NSStringFromSelector([item action]);
1144
1145        if ([unwantedActions containsObject:actionString])
1146            continue;
1147
1148        // Copy items since a menu item can be in only one menu at a time, and we don't
1149        // want to modify the original menu supplied by PDFKit.
1150        NSMenuItem *itemCopy = [item copy];
1151        [copiedItems addObject:itemCopy];
1152
1153        // Include all of PDFKit's separators for now. At the end we'll remove any ones that were made
1154        // useless by removing PDFKit's menu items.
1155        if ([itemCopy isSeparatorItem])
1156            continue;
1157
1158        NSNumber *tagNumber = [actionsToTags objectForKey:actionString];
1159
1160        int tag;
1161        if (tagNumber != nil)
1162            tag = [tagNumber intValue];
1163        else {
1164            // This should happen only if PDFKit updates behind WebKit's back. It's non-ideal because clients that only include tags
1165            // that they recognize (like Safari) won't get these PDFKit additions until WebKit is updated to match.
1166            tag = WebMenuItemTagOther;
1167            LOG_ERROR("no WebKit menu item tag found for PDF context menu item action \"%@\", using WebMenuItemTagOther", actionString);
1168        }
1169
1170        if ([itemCopy tag] == 0) {
1171            [itemCopy setTag:tag];
1172            if ([itemCopy target] == PDFSubview) {
1173                // Note that updating the defaults is cheap because it catches redundant settings, so installing
1174                // the proxy for actions that don't impact the defaults is OK
1175                [itemCopy setTarget:PDFSubviewProxy];
1176            }
1177        } else
1178            LOG_ERROR("PDF context menu item %@ came with tag %d, so no WebKit tag was applied. This could mean that the item doesn't appear in clients such as Safari.", [itemCopy title], [itemCopy tag]);
1179    }
1180
1181    [actionsToTags release];
1182    [unwantedActions release];
1183
1184    // Since we might have removed elements supplied by PDFKit, and we want to minimize our hardwired
1185    // knowledge of the order and arrangement of PDFKit's menu items, we need to remove any bogus
1186    // separators that were left behind.
1187    [copiedItems _webkit_removeUselessMenuItemSeparators];
1188
1189    return copiedItems;
1190}
1191
1192- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection
1193{
1194    if (![string length])
1195        return nil;
1196
1197    int options = 0;
1198    if (!forward)
1199        options |= NSBackwardsSearch;
1200
1201    if (!caseFlag)
1202        options |= NSCaseInsensitiveSearch;
1203
1204    PDFDocument *document = [PDFSubview document];
1205
1206    PDFSelection *selectionForInitialSearch = [initialSelection copy];
1207    if (startInSelection) {
1208        // Initially we want to include the selected text in the search. PDFDocument's API always searches from just
1209        // past the passed-in selection, so we need to pass a selection that's modified appropriately.
1210        // FIXME 4182863: Ideally we'd use a zero-length selection at the edge of the current selection, but zero-length
1211        // selections don't work in PDFDocument. So instead we make a one-length selection just before or after the
1212        // current selection, which works for our purposes even when the current selection is at an edge of the
1213        // document.
1214        int initialSelectionLength = [[initialSelection string] length];
1215        if (forward) {
1216            [selectionForInitialSearch extendSelectionAtStart:1];
1217            [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
1218        } else {
1219            [selectionForInitialSearch extendSelectionAtEnd:1];
1220            [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
1221        }
1222    }
1223    PDFSelection *foundSelection = [document findString:string fromSelection:selectionForInitialSearch withOptions:options];
1224    [selectionForInitialSearch release];
1225
1226    // If we first searched in the selection, and we found the selection, search again from just past the selection
1227    if (startInSelection && _PDFSelectionsAreEqual(foundSelection, initialSelection))
1228        foundSelection = [document findString:string fromSelection:initialSelection withOptions:options];
1229
1230    if (!foundSelection && wrapFlag)
1231        foundSelection = [document findString:string fromSelection:nil withOptions:options];
1232
1233    return foundSelection;
1234}
1235
1236- (void)_openWithFinder:(id)sender
1237{
1238    // We don't want to write the file until we have a document to write (see 4892525).
1239    if (![PDFSubview document]) {
1240        NSBeep();
1241        return;
1242    }
1243
1244    NSString *opath = [self _path];
1245
1246    if (opath) {
1247        if (!written) {
1248            // Create a PDF file with the minimal permissions (only accessible to the current user, see 4145714)
1249            NSNumber *permissions = [[NSNumber alloc] initWithInt:S_IRUSR];
1250            NSDictionary *fileAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:permissions, NSFilePosixPermissions, nil];
1251            [permissions release];
1252
1253            [[NSFileManager defaultManager] createFileAtPath:opath contents:[dataSource data] attributes:fileAttributes];
1254
1255            [fileAttributes release];
1256            written = YES;
1257        }
1258
1259        if (![[NSWorkspace sharedWorkspace] openFile:opath]) {
1260            // NSWorkspace couldn't open file.  Do we need an alert
1261            // here?  We ignore the error elsewhere.
1262        }
1263    }
1264}
1265
1266- (NSString *)_path
1267{
1268    // Generate path once.
1269    if (path)
1270        return path;
1271
1272    NSString *filename = [[dataSource response] suggestedFilename];
1273    NSFileManager *manager = [NSFileManager defaultManager];
1274    NSString *temporaryPDFDirectoryPath = [self _temporaryPDFDirectoryPath];
1275
1276    if (!temporaryPDFDirectoryPath) {
1277        // This should never happen; if it does we'll fail silently on non-debug builds.
1278        ASSERT_NOT_REACHED();
1279        return nil;
1280    }
1281
1282    path = [temporaryPDFDirectoryPath stringByAppendingPathComponent:filename];
1283    if ([manager fileExistsAtPath:path]) {
1284        NSString *pathTemplatePrefix = [temporaryPDFDirectoryPath stringByAppendingPathComponent:@"XXXXXX-"];
1285        NSString *pathTemplate = [pathTemplatePrefix stringByAppendingString:filename];
1286        // fileSystemRepresentation returns a const char *; copy it into a char * so we can modify it safely
1287        char *cPath = strdup([pathTemplate fileSystemRepresentation]);
1288        int fd = mkstemps(cPath, strlen(cPath) - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1);
1289        if (fd < 0) {
1290            // Couldn't create a temporary file! Should never happen; if it does we'll fail silently on non-debug builds.
1291            ASSERT_NOT_REACHED();
1292            path = nil;
1293        } else {
1294            close(fd);
1295            path = [manager stringWithFileSystemRepresentation:cPath length:strlen(cPath)];
1296        }
1297        free(cPath);
1298    }
1299
1300    [path retain];
1301
1302    return path;
1303}
1304
1305- (void)_PDFDocumentViewMightHaveScrolled:(NSNotification *)notification
1306{
1307    NSClipView *clipView = [self _clipViewForPDFDocumentView];
1308    ASSERT([notification object] == clipView);
1309
1310    NSPoint scrollPosition = [clipView bounds].origin;
1311    if (NSEqualPoints(scrollPosition, lastScrollPosition))
1312        return;
1313
1314    lastScrollPosition = scrollPosition;
1315    WebView *webView = [self _webView];
1316    [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[[dataSource webFrame] frameView]];
1317}
1318
1319- (PDFView *)_PDFSubview
1320{
1321    return PDFSubview;
1322}
1323
1324- (BOOL)_pointIsInSelection:(NSPoint)point
1325{
1326    PDFPage *page = [PDFSubview pageForPoint:point nearest:NO];
1327    if (!page)
1328        return NO;
1329
1330    NSRect selectionRect = [PDFSubview convertRect:[[PDFSubview currentSelection] boundsForPage:page] fromPage:page];
1331
1332    return NSPointInRect(point, selectionRect);
1333}
1334
1335- (void)_scaleOrDisplayModeOrPageChanged:(NSNotification *)notification
1336{
1337    ASSERT([notification object] == PDFSubview);
1338    if (!_ignoreScaleAndDisplayModeAndPageNotifications) {
1339        [self _updatePreferencesSoon];
1340        // Notify UI delegate that the entire page has been redrawn, since (unlike for WebHTMLView)
1341        // we can't hook into the drawing mechanism itself. This fixes 5337529.
1342        WebView *webView = [self _webView];
1343        [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView bounds]];
1344    }
1345}
1346
1347- (NSAttributedString *)_scaledAttributedString:(NSAttributedString *)unscaledAttributedString
1348{
1349    if (!unscaledAttributedString)
1350        return nil;
1351
1352    float scaleFactor = [PDFSubview scaleFactor];
1353    if (scaleFactor == 1.0)
1354        return unscaledAttributedString;
1355
1356    NSMutableAttributedString *result = [[unscaledAttributedString mutableCopy] autorelease];
1357    unsigned int length = [result length];
1358    NSRange effectiveRange = NSMakeRange(0,0);
1359
1360    [result beginEditing];
1361    while (NSMaxRange(effectiveRange) < length) {
1362        NSFont *unscaledFont = [result attribute:NSFontAttributeName atIndex:NSMaxRange(effectiveRange) effectiveRange:&effectiveRange];
1363
1364        if (!unscaledFont) {
1365            // FIXME: We can't scale the font if we don't know what it is. We should always know what it is,
1366            // but sometimes don't due to PDFKit issue 5089411. When that's addressed, we can remove this
1367            // early continue.
1368            LOG_ERROR("no font attribute found in range %@ for attributed string \"%@\" on page %@ (see radar 5089411)", NSStringFromRange(effectiveRange), result, [[dataSource request] URL]);
1369            continue;
1370        }
1371
1372        NSFont *scaledFont = [NSFont fontWithName:[unscaledFont fontName] size:[unscaledFont pointSize]*scaleFactor];
1373        [result addAttribute:NSFontAttributeName value:scaledFont range:effectiveRange];
1374    }
1375    [result endEditing];
1376
1377    return result;
1378}
1379
1380- (void)_setTextMatches:(NSArray *)array
1381{
1382    [array retain];
1383    [textMatches release];
1384    textMatches = array;
1385}
1386
1387- (NSString *)_temporaryPDFDirectoryPath
1388{
1389    // Returns nil if the temporary PDF directory didn't exist and couldn't be created
1390
1391    static NSString *_temporaryPDFDirectoryPath = nil;
1392
1393    if (!_temporaryPDFDirectoryPath) {
1394        NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPDFs-XXXXXX"];
1395        char *cTemplate = strdup([temporaryDirectoryTemplate fileSystemRepresentation]);
1396
1397        if (!mkdtemp(cTemplate)) {
1398            // This should never happen; if it does we'll fail silently on non-debug builds.
1399            ASSERT_NOT_REACHED();
1400        } else {
1401            // cTemplate has now been modified to be the just-created directory name. This directory has 700 permissions,
1402            // so only the current user can add to it or view its contents.
1403            _temporaryPDFDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:cTemplate length:strlen(cTemplate)] retain];
1404        }
1405
1406        free(cTemplate);
1407    }
1408
1409    return _temporaryPDFDirectoryPath;
1410}
1411
1412- (void)_trackFirstResponder
1413{
1414    ASSERT([self window]);
1415    BOOL newFirstResponderIsPDFDocumentView = [[self window] firstResponder] == [PDFSubview documentView];
1416    if (newFirstResponderIsPDFDocumentView == firstResponderIsPDFDocumentView)
1417        return;
1418
1419    // This next clause is the entire purpose of _trackFirstResponder. In other WebDocument
1420    // view classes this is done in a resignFirstResponder override, but in this case the
1421    // first responder view is a PDFKit class that we can't subclass.
1422    if (newFirstResponderIsPDFDocumentView && ![[dataSource _webView] maintainsInactiveSelection])
1423        [self deselectAll];
1424
1425    firstResponderIsPDFDocumentView = newFirstResponderIsPDFDocumentView;
1426}
1427
1428- (void)_updatePreferences:(WebPreferences *)prefs
1429{
1430    float scaleFactor = [PDFSubview autoScales] ? 0.0f : [PDFSubview scaleFactor];
1431    [prefs setPDFScaleFactor:scaleFactor];
1432    [prefs setPDFDisplayMode:[PDFSubview displayMode]];
1433    _willUpdatePreferencesSoon = NO;
1434    [prefs release];
1435    [self release];
1436}
1437
1438- (void)_updatePreferencesSoon
1439{
1440    // Consolidate calls; due to the PDFPrefUpdatingProxy method, this can be called multiple times with a single user action
1441    // such as showing the context menu.
1442    if (_willUpdatePreferencesSoon)
1443        return;
1444
1445    WebPreferences *prefs = [[dataSource _webView] preferences];
1446
1447    [self retain];
1448    [prefs retain];
1449    [self performSelector:@selector(_updatePreferences:) withObject:prefs afterDelay:0];
1450    _willUpdatePreferencesSoon = YES;
1451}
1452
1453- (NSSet *)_visiblePDFPages
1454{
1455    // Returns the set of pages that are at least partly visible, used to avoid processing non-visible pages
1456    PDFDocument *pdfDocument = [PDFSubview document];
1457    if (!pdfDocument)
1458        return nil;
1459
1460    NSRect pdfViewBounds = [PDFSubview bounds];
1461    PDFPage *topLeftPage = [PDFSubview pageForPoint:NSMakePoint(NSMinX(pdfViewBounds), NSMaxY(pdfViewBounds)) nearest:YES];
1462    PDFPage *bottomRightPage = [PDFSubview pageForPoint:NSMakePoint(NSMaxX(pdfViewBounds), NSMinY(pdfViewBounds)) nearest:YES];
1463
1464    // only page-free documents should return nil for either of these two since we passed YES for nearest:
1465    if (!topLeftPage) {
1466        ASSERT(!bottomRightPage);
1467        return nil;
1468    }
1469
1470    NSUInteger firstVisiblePageIndex = [pdfDocument indexForPage:topLeftPage];
1471    NSUInteger lastVisiblePageIndex = [pdfDocument indexForPage:bottomRightPage];
1472
1473    if (firstVisiblePageIndex > lastVisiblePageIndex) {
1474        NSUInteger swap = firstVisiblePageIndex;
1475        firstVisiblePageIndex = lastVisiblePageIndex;
1476        lastVisiblePageIndex = swap;
1477    }
1478
1479    NSMutableSet *result = [NSMutableSet set];
1480    NSUInteger pageIndex;
1481    for (pageIndex = firstVisiblePageIndex; pageIndex <= lastVisiblePageIndex; ++pageIndex)
1482        [result addObject:[pdfDocument pageAtIndex:pageIndex]];
1483
1484    return result;
1485}
1486
1487@end
1488
1489@implementation PDFPrefUpdatingProxy
1490
1491- (id)initWithView:(WebPDFView *)aView
1492{
1493    // No [super init], since we inherit from NSProxy
1494    view = aView;
1495    return self;
1496}
1497
1498- (void)forwardInvocation:(NSInvocation *)invocation
1499{
1500    [invocation invokeWithTarget:[view _PDFSubview]];
1501    [view _updatePreferencesSoon];
1502}
1503
1504- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
1505{
1506    return [[view _PDFSubview] methodSignatureForSelector:sel];
1507}
1508
1509@end
1510