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