Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2010, 2011 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  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #import "config.h"
     27 #import "PDFViewController.h"
     28 
     29 #import "DataReference.h"
     30 #import "WKAPICast.h"
     31 #import "WKView.h"
     32 #import "WebData.h"
     33 #import "WebEventFactory.h"
     34 #import "WebPageGroup.h"
     35 #import "WebPageProxy.h"
     36 #import "WebPreferences.h"
     37 #import <PDFKit/PDFKit.h>
     38 #import <WebCore/LocalizedStrings.h>
     39 #import <wtf/text/WTFString.h>
     40 
     41 // Redeclarations of PDFKit notifications. We can't use the API since we use a weak link to the framework.
     42 #define _webkit_PDFViewDisplayModeChangedNotification @"PDFViewDisplayModeChanged"
     43 #define _webkit_PDFViewScaleChangedNotification @"PDFViewScaleChanged"
     44 #define _webkit_PDFViewPageChangedNotification @"PDFViewChangedPage"
     45 
     46 using namespace WebKit;
     47 
     48 @class PDFDocument;
     49 @class PDFView;
     50 
     51 @interface PDFDocument (PDFDocumentDetails)
     52 - (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate;
     53 @end
     54 
     55 extern "C" NSString *_NSPathForSystemFramework(NSString *framework);
     56 
     57 // MARK: C UTILITY FUNCTIONS
     58 
     59 static void _applicationInfoForMIMEType(NSString *type, NSString **name, NSImage **image)
     60 {
     61     ASSERT(name);
     62     ASSERT(image);
     63 
     64     CFURLRef appURL = 0;
     65 
     66     OSStatus error = LSCopyApplicationForMIMEType((CFStringRef)type, kLSRolesAll, &appURL);
     67     if (error != noErr)
     68         return;
     69 
     70     NSString *appPath = [(NSURL *)appURL path];
     71     if (appURL)
     72         CFRelease(appURL);
     73 
     74     *image = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
     75     [*image setSize:NSMakeSize(16, 16)];
     76 
     77     *name = [[NSFileManager defaultManager] displayNameAtPath:appPath];
     78 }
     79 
     80 // FIXME 4182876: We can eliminate this function in favor if -isEqual: if [PDFSelection isEqual:] is overridden
     81 // to compare contents.
     82 static BOOL _PDFSelectionsAreEqual(PDFSelection *selectionA, PDFSelection *selectionB)
     83 {
     84     NSArray *aPages = [selectionA pages];
     85     NSArray *bPages = [selectionB pages];
     86 
     87     if (![aPages isEqual:bPages])
     88         return NO;
     89 
     90     NSUInteger count = [aPages count];
     91     for (NSUInteger i = 0; i < count; ++i) {
     92         NSRect aBounds = [selectionA boundsForPage:[aPages objectAtIndex:i]];
     93         NSRect bBounds = [selectionB boundsForPage:[bPages objectAtIndex:i]];
     94         if (!NSEqualRects(aBounds, bBounds))
     95             return NO;
     96     }
     97 
     98     return YES;
     99 }
    100 
    101 @interface WKPDFView : NSView
    102 {
    103     PDFViewController* _pdfViewController;
    104 
    105     RetainPtr<NSView> _pdfPreviewView;
    106     PDFView *_pdfView;
    107     BOOL _ignoreScaleAndDisplayModeAndPageNotifications;
    108     BOOL _willUpdatePreferencesSoon;
    109 }
    110 
    111 - (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController;
    112 - (void)invalidate;
    113 - (PDFView *)pdfView;
    114 - (void)setDocument:(PDFDocument *)pdfDocument;
    115 
    116 - (void)_applyPDFPreferences;
    117 - (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection;
    118 @end
    119 
    120 @implementation WKPDFView
    121 
    122 - (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController
    123 {
    124     if ((self = [super initWithFrame:frame])) {
    125         _pdfViewController = pdfViewController;
    126 
    127         [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    128 
    129         Class previewViewClass = PDFViewController::pdfPreviewViewClass();
    130         ASSERT(previewViewClass);
    131 
    132         _pdfPreviewView.adoptNS([[previewViewClass alloc] initWithFrame:frame]);
    133         [_pdfPreviewView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    134         [self addSubview:_pdfPreviewView.get()];
    135 
    136         _pdfView = [_pdfPreviewView.get() performSelector:@selector(pdfView)];
    137         [_pdfView setDelegate:self];
    138     }
    139 
    140     return self;
    141 }
    142 
    143 - (void)invalidate
    144 {
    145     _pdfViewController = 0;
    146 }
    147 
    148 - (PDFView *)pdfView
    149 {
    150     return _pdfView;
    151 }
    152 
    153 - (void)setDocument:(PDFDocument *)pdfDocument
    154 {
    155     _ignoreScaleAndDisplayModeAndPageNotifications = YES;
    156     [_pdfView setDocument:pdfDocument];
    157     [self _applyPDFPreferences];
    158     _ignoreScaleAndDisplayModeAndPageNotifications = NO;
    159 }
    160 
    161 - (void)_applyPDFPreferences
    162 {
    163     if (!_pdfViewController)
    164         return;
    165 
    166     WebPreferences *preferences = _pdfViewController->page()->pageGroup()->preferences();
    167 
    168     CGFloat scaleFactor = preferences->pdfScaleFactor();
    169     if (!scaleFactor)
    170         [_pdfView setAutoScales:YES];
    171     else {
    172         [_pdfView setAutoScales:NO];
    173         [_pdfView setScaleFactor:scaleFactor];
    174     }
    175     [_pdfView setDisplayMode:preferences->pdfDisplayMode()];
    176 }
    177 
    178 - (void)_updatePreferences:(id)ignored
    179 {
    180     _willUpdatePreferencesSoon = NO;
    181 
    182     if (!_pdfViewController)
    183         return;
    184 
    185     WebPreferences* preferences = _pdfViewController->page()->pageGroup()->preferences();
    186 
    187     CGFloat scaleFactor = [_pdfView autoScales] ? 0 : [_pdfView scaleFactor];
    188     preferences->setPDFScaleFactor(scaleFactor);
    189     preferences->setPDFDisplayMode([_pdfView displayMode]);
    190 }
    191 
    192 - (void)_updatePreferencesSoon
    193 {
    194     if (_willUpdatePreferencesSoon)
    195         return;
    196 
    197     [self performSelector:@selector(_updatePreferences:) withObject:nil afterDelay:0];
    198     _willUpdatePreferencesSoon = YES;
    199 }
    200 
    201 - (void)_scaleOrDisplayModeOrPageChanged:(NSNotification *)notification
    202 {
    203     ASSERT_ARG(notification, [notification object] == _pdfView);
    204     if (!_ignoreScaleAndDisplayModeAndPageNotifications)
    205         [self _updatePreferencesSoon];
    206 }
    207 
    208 - (void)_openWithFinder:(id)sender
    209 {
    210     _pdfViewController->openPDFInFinder();
    211 }
    212 
    213 - (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection
    214 {
    215     if (![string length])
    216         return nil;
    217 
    218     int options = 0;
    219     if (!forward)
    220         options |= NSBackwardsSearch;
    221 
    222     if (!caseFlag)
    223         options |= NSCaseInsensitiveSearch;
    224 
    225     PDFDocument *document = [_pdfView document];
    226 
    227     PDFSelection *selectionForInitialSearch = [initialSelection copy];
    228     if (startInSelection) {
    229         // Initially we want to include the selected text in the search.  So we must modify the starting search
    230         // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before
    231         // the current selection (if searching forwards) or after (if searching backwards).
    232         int initialSelectionLength = [[initialSelection string] length];
    233         if (forward) {
    234             [selectionForInitialSearch extendSelectionAtStart:1];
    235             [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
    236         } else {
    237             [selectionForInitialSearch extendSelectionAtEnd:1];
    238             [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
    239         }
    240     }
    241     PDFSelection *foundSelection = [document findString:string fromSelection:selectionForInitialSearch withOptions:options];
    242     [selectionForInitialSearch release];
    243 
    244     // If we first searched in the selection, and we found the selection, search again from just past the selection
    245     if (startInSelection && _PDFSelectionsAreEqual(foundSelection, initialSelection))
    246         foundSelection = [document findString:string fromSelection:initialSelection withOptions:options];
    247 
    248     if (!foundSelection && wrapFlag)
    249         foundSelection = [document findString:string fromSelection:nil withOptions:options];
    250 
    251     return foundSelection;
    252 }
    253 
    254 - (NSUInteger)_countMatches:(NSString *)string caseSensitive:(BOOL)caseFlag
    255 {
    256     if (![string length])
    257         return 0;
    258 
    259     int options = caseFlag ? 0 : NSCaseInsensitiveSearch;
    260 
    261     return [[[_pdfView document] findString:string withOptions:options] count];
    262 }
    263 
    264 // MARK: NSView overrides
    265 
    266 - (void)viewDidMoveToWindow
    267 {
    268     if (![self window])
    269         return;
    270 
    271     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    272     [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewScaleChangedNotification object:_pdfView];
    273     [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView];
    274     [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewPageChangedNotification object:_pdfView];
    275 }
    276 
    277 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
    278 {
    279     if (![self window])
    280         return;
    281 
    282     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    283     [notificationCenter removeObserver:self name:_webkit_PDFViewScaleChangedNotification object:_pdfView];
    284     [notificationCenter removeObserver:self name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView];
    285     [notificationCenter removeObserver:self name:_webkit_PDFViewPageChangedNotification object:_pdfView];
    286 }
    287 
    288 - (NSView *)hitTest:(NSPoint)point
    289 {
    290     // Override hitTest so we can override menuForEvent.
    291     NSEvent *event = [NSApp currentEvent];
    292     NSEventType type = [event type];
    293     if (type == NSRightMouseDown || (type == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask)))
    294         return self;
    295 
    296     return [super hitTest:point];
    297 }
    298 
    299 - (NSMenu *)menuForEvent:(NSEvent *)theEvent
    300 {
    301     NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
    302 
    303     NSEnumerator *menuItemEnumerator = [[[_pdfView menuForEvent:theEvent] itemArray] objectEnumerator];
    304     while (NSMenuItem *item = [menuItemEnumerator nextObject]) {
    305         NSMenuItem *itemCopy = [item copy];
    306         [menu addItem:itemCopy];
    307         [itemCopy release];
    308 
    309         if ([item action] != @selector(copy:))
    310             continue;
    311 
    312         // Add in an "Open with <default PDF viewer>" item
    313         NSString *appName = nil;
    314         NSImage *appIcon = nil;
    315 
    316         _applicationInfoForMIMEType(@"application/pdf", &appName, &appIcon);
    317         if (!appName)
    318             appName = WEB_UI_STRING("Finder", "Default application name for Open With context menu");
    319 
    320         // To match the PDFKit style, we'll add Open with Preview even when there's no document yet to view, and
    321         // disable it using validateUserInterfaceItem.
    322         NSString *title = [NSString stringWithFormat:WEB_UI_STRING("Open with %@", "context menu item for PDF"), appName];
    323 
    324         item = [[NSMenuItem alloc] initWithTitle:title action:@selector(_openWithFinder:) keyEquivalent:@""];
    325         if (appIcon)
    326             [item setImage:appIcon];
    327         [menu addItem:[NSMenuItem separatorItem]];
    328         [menu addItem:item];
    329         [item release];
    330     }
    331 
    332     return [menu autorelease];
    333 }
    334 
    335 // MARK: NSUserInterfaceValidations PROTOCOL IMPLEMENTATION
    336 
    337 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
    338 {
    339     SEL action = [item action];
    340     if (action == @selector(_openWithFinder:))
    341         return [_pdfView document] != nil;
    342     return YES;
    343 }
    344 
    345 // MARK: PDFView delegate methods
    346 
    347 - (void)PDFViewWillClickOnLink:(PDFView *)sender withURL:(NSURL *)URL
    348 {
    349     _pdfViewController->linkClicked([URL absoluteString]);
    350 }
    351 
    352 - (void)PDFViewOpenPDFInNativeApplication:(PDFView *)sender
    353 {
    354     _pdfViewController->openPDFInFinder();
    355 }
    356 
    357 - (void)PDFViewSavePDFToDownloadFolder:(PDFView *)sender
    358 {
    359     _pdfViewController->savePDFToDownloadsFolder();
    360 }
    361 
    362 @end
    363 
    364 namespace WebKit {
    365 
    366 PassOwnPtr<PDFViewController> PDFViewController::create(WKView *wkView)
    367 {
    368     return adoptPtr(new PDFViewController(wkView));
    369 }
    370 
    371 PDFViewController::PDFViewController(WKView *wkView)
    372     : m_wkView(wkView)
    373     , m_wkPDFView(AdoptNS, [[WKPDFView alloc] initWithFrame:[m_wkView bounds] PDFViewController:this])
    374     , m_pdfView([m_wkPDFView.get() pdfView])
    375     , m_hasWrittenPDFToDisk(false)
    376 {
    377     [m_wkView addSubview:m_wkPDFView.get()];
    378 }
    379 
    380 PDFViewController::~PDFViewController()
    381 {
    382     [m_wkPDFView.get() removeFromSuperview];
    383     [m_wkPDFView.get() invalidate];
    384     m_wkPDFView = nullptr;
    385 }
    386 
    387 WebPageProxy* PDFViewController::page() const
    388 {
    389     return toImpl([m_wkView pageRef]);
    390 }
    391 
    392 NSView* PDFViewController::pdfView() const
    393 {
    394     return m_wkPDFView.get();
    395 }
    396 
    397 static RetainPtr<CFDataRef> convertPostScriptDataSourceToPDF(const CoreIPC::DataReference& dataReference)
    398 {
    399     // Convert PostScript to PDF using Quartz 2D API
    400     // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html
    401 
    402     CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 };
    403     RetainPtr<CGPSConverterRef> converter(AdoptCF, CGPSConverterCreate(0, &callbacks, 0));
    404     ASSERT(converter);
    405 
    406     RetainPtr<NSData> nsData(AdoptNS, [[NSData alloc] initWithBytesNoCopy:const_cast<uint8_t*>(dataReference.data()) length:dataReference.size() freeWhenDone:NO]);
    407 
    408     RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData((CFDataRef)nsData.get()));
    409     ASSERT(provider);
    410 
    411     RetainPtr<CFMutableDataRef> result(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
    412     ASSERT(result);
    413 
    414     RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreateWithCFData(result.get()));
    415     ASSERT(consumer);
    416 
    417     CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0);
    418 
    419     if (!result)
    420         return 0;
    421 
    422     return result;
    423 }
    424 
    425 void PDFViewController::setPDFDocumentData(const String& mimeType, const String& suggestedFilename, const CoreIPC::DataReference& dataReference)
    426 {
    427     if (equalIgnoringCase(mimeType, "application/postscript")) {
    428         m_pdfData = convertPostScriptDataSourceToPDF(dataReference);
    429         if (!m_pdfData)
    430             return;
    431     } else {
    432         // Make sure to copy the data.
    433         m_pdfData.adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size()));
    434     }
    435 
    436     m_suggestedFilename = suggestedFilename;
    437 
    438     RetainPtr<PDFDocument> pdfDocument(AdoptNS, [[pdfDocumentClass() alloc] initWithData:(NSData *)m_pdfData.get()]);
    439     [m_wkPDFView.get() setDocument:pdfDocument.get()];
    440 }
    441 
    442 double PDFViewController::zoomFactor() const
    443 {
    444     return [m_pdfView scaleFactor];
    445 }
    446 
    447 void PDFViewController::setZoomFactor(double zoomFactor)
    448 {
    449     [m_pdfView setScaleFactor:zoomFactor];
    450 }
    451 
    452 Class PDFViewController::pdfDocumentClass()
    453 {
    454     static Class pdfDocumentClass = [pdfKitBundle() classNamed:@"PDFDocument"];
    455 
    456     return pdfDocumentClass;
    457 }
    458 
    459 Class PDFViewController::pdfPreviewViewClass()
    460 {
    461     static Class pdfPreviewViewClass = [pdfKitBundle() classNamed:@"PDFPreviewView"];
    462 
    463     return pdfPreviewViewClass;
    464 }
    465 
    466 NSBundle* PDFViewController::pdfKitBundle()
    467 {
    468     static NSBundle *pdfKitBundle;
    469     if (pdfKitBundle)
    470         return pdfKitBundle;
    471 
    472     NSString *pdfKitPath = [_NSPathForSystemFramework(@"Quartz.framework") stringByAppendingString:@"/Frameworks/PDFKit.framework"];
    473     if (!pdfKitPath) {
    474         LOG_ERROR("Couldn't find PDFKit.framework");
    475         return nil;
    476     }
    477 
    478     pdfKitBundle = [NSBundle bundleWithPath:pdfKitPath];
    479     if (![pdfKitBundle load])
    480         LOG_ERROR("Couldn't load PDFKit.framework");
    481     return pdfKitBundle;
    482 }
    483 
    484 NSPrintOperation *PDFViewController::makePrintOperation(NSPrintInfo *printInfo)
    485 {
    486     return [[m_pdfView document] getPrintOperationForPrintInfo:printInfo autoRotate:YES];
    487 }
    488 
    489 void PDFViewController::openPDFInFinder()
    490 {
    491     // We don't want to open the PDF until we have a document to write. (see 4892525).
    492     if (![m_pdfView document]) {
    493         NSBeep();
    494         return;
    495     }
    496 
    497     NSString *path = pathToPDFOnDisk();
    498     if (!path)
    499         return;
    500 
    501     if (!m_hasWrittenPDFToDisk) {
    502         // Create a PDF file with the minimal permissions (only accessible to the current user, see 4145714).
    503         RetainPtr<NSNumber> permissions(AdoptNS, [[NSNumber alloc] initWithInt:S_IRUSR]);
    504         RetainPtr<NSDictionary> fileAttributes(AdoptNS, [[NSDictionary alloc] initWithObjectsAndKeys:permissions.get(), NSFilePosixPermissions, nil]);
    505 
    506         if (![[NSFileManager defaultManager] createFileAtPath:path contents:(NSData *)m_pdfData.get() attributes:fileAttributes.get()])
    507             return;
    508 
    509         m_hasWrittenPDFToDisk = true;
    510     }
    511 
    512     [[NSWorkspace sharedWorkspace] openFile:path];
    513 }
    514 
    515 static void releaseCFData(unsigned char*, const void* data)
    516 {
    517     ASSERT(CFGetTypeID(data) == CFDataGetTypeID());
    518 
    519     // Balanced by CFRetain in savePDFToDownloadsFolder.
    520     CFRelease(data);
    521 }
    522 
    523 void PDFViewController::savePDFToDownloadsFolder()
    524 {
    525     // We don't want to write the file until we have a document to write. (see 5267607).
    526     if (![m_pdfView document]) {
    527         NSBeep();
    528         return;
    529     }
    530 
    531     ASSERT(m_pdfData);
    532 
    533     // Balanced by CFRelease in releaseCFData.
    534     CFRetain(m_pdfData.get());
    535 
    536     RefPtr<WebData> data = WebData::createWithoutCopying(CFDataGetBytePtr(m_pdfData.get()), CFDataGetLength(m_pdfData.get()), releaseCFData, m_pdfData.get());
    537 
    538     page()->saveDataToFileInDownloadsFolder(m_suggestedFilename.get(), page()->mainFrame()->mimeType(), page()->mainFrame()->url(), data.get());
    539 }
    540 
    541 static NSString *temporaryPDFDirectoryPath()
    542 {
    543     static NSString *temporaryPDFDirectoryPath;
    544 
    545     if (!temporaryPDFDirectoryPath) {
    546         NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPDFs-XXXXXX"];
    547         CString templateRepresentation = [temporaryDirectoryTemplate fileSystemRepresentation];
    548 
    549         if (mkdtemp(templateRepresentation.mutableData()))
    550             temporaryPDFDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:templateRepresentation.data() length:templateRepresentation.length()] copy];
    551     }
    552 
    553     return temporaryPDFDirectoryPath;
    554 }
    555 
    556 NSString *PDFViewController::pathToPDFOnDisk()
    557 {
    558     if (m_pathToPDFOnDisk)
    559         return m_pathToPDFOnDisk.get();
    560 
    561     NSString *pdfDirectoryPath = temporaryPDFDirectoryPath();
    562     if (!pdfDirectoryPath)
    563         return nil;
    564 
    565     NSString *path = [pdfDirectoryPath stringByAppendingPathComponent:m_suggestedFilename.get()];
    566 
    567     NSFileManager *fileManager = [NSFileManager defaultManager];
    568     if ([fileManager fileExistsAtPath:path]) {
    569         NSString *pathTemplatePrefix = [pdfDirectoryPath stringByAppendingString:@"XXXXXX-"];
    570         NSString *pathTemplate = [pathTemplatePrefix stringByAppendingPathComponent:m_suggestedFilename.get()];
    571         CString pathTemplateRepresentation = [pathTemplate fileSystemRepresentation];
    572 
    573         int fd = mkstemps(pathTemplateRepresentation.mutableData(), pathTemplateRepresentation.length() - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1);
    574         if (fd < 0)
    575             return nil;
    576 
    577         close(fd);
    578         path = [fileManager stringWithFileSystemRepresentation:pathTemplateRepresentation.data() length:pathTemplateRepresentation.length()];
    579     }
    580 
    581     m_pathToPDFOnDisk.adoptNS([path copy]);
    582     return path;
    583 }
    584 
    585 void PDFViewController::linkClicked(const String& url)
    586 {
    587     NSEvent* nsEvent = [NSApp currentEvent];
    588     WebMouseEvent event;
    589     switch ([nsEvent type]) {
    590     case NSLeftMouseUp:
    591     case NSRightMouseUp:
    592     case NSOtherMouseUp:
    593         event = WebEventFactory::createWebMouseEvent(nsEvent, m_pdfView);
    594     default:
    595         // For non mouse-clicks or for keyboard events, pass an empty WebMouseEvent
    596         // through.  The event is only used by the WebFrameLoaderClient to determine
    597         // the modifier keys and which mouse button is down.  These queries will be
    598         // valid with an empty event.
    599         break;
    600     }
    601 
    602     page()->linkClicked(url, event);
    603 }
    604 
    605 void PDFViewController::findString(const String& string, FindOptions options, unsigned maxMatchCount)
    606 {
    607     BOOL forward = !(options & FindOptionsBackwards);
    608     BOOL caseFlag = !(options & FindOptionsCaseInsensitive);
    609     BOOL wrapFlag = options & FindOptionsWrapAround;
    610 
    611     PDFSelection *selection = [m_wkPDFView.get() _nextMatchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag fromSelection:[m_pdfView currentSelection] startInSelection:NO];
    612     NSUInteger matchCount = [m_wkPDFView.get() _countMatches:string caseSensitive:caseFlag];
    613     if (matchCount > maxMatchCount)
    614         matchCount = maxMatchCount;
    615 
    616     if (!selection) {
    617         page()->didFailToFindString(string);
    618         return;
    619     }
    620 
    621     [m_pdfView setCurrentSelection:selection];
    622     [m_pdfView scrollSelectionToVisible:nil];
    623     page()->didFindString(string, matchCount);
    624 }
    625 
    626 void PDFViewController::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount)
    627 {
    628     BOOL caseFlag = !(options & FindOptionsCaseInsensitive);
    629 
    630     NSUInteger matchCount = [m_wkPDFView.get() _countMatches:string caseSensitive:caseFlag];
    631     if (matchCount > maxMatchCount)
    632         matchCount = maxMatchCount;
    633     page()->didCountStringMatches(string, matchCount);
    634 }
    635 
    636 } // namespace WebKit
    637