summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/UIProcess/API/mac/PDFViewController.mm')
-rw-r--r--Source/WebKit2/UIProcess/API/mac/PDFViewController.mm671
1 files changed, 671 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm
new file mode 100644
index 000000000..c5441a49f
--- /dev/null
+++ b/Source/WebKit2/UIProcess/API/mac/PDFViewController.mm
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "PDFViewController.h"
+
+#import "DataReference.h"
+#import "WKAPICast.h"
+#import "WKViewPrivate.h"
+#import "WebData.h"
+#import "WebEventFactory.h"
+#import "WebPageGroup.h"
+#import "WebPageProxy.h"
+#import "WebPreferences.h"
+#import <PDFKit/PDFKit.h>
+#import <WebCore/LocalizedStrings.h>
+#import <wtf/text/CString.h>
+#import <wtf/text/WTFString.h>
+
+// Redeclarations of PDFKit notifications. We can't use the API since we use a weak link to the framework.
+#define _webkit_PDFViewDisplayModeChangedNotification @"PDFViewDisplayModeChanged"
+#define _webkit_PDFViewScaleChangedNotification @"PDFViewScaleChanged"
+#define _webkit_PDFViewPageChangedNotification @"PDFViewChangedPage"
+
+using namespace WebKit;
+
+@class PDFDocument;
+@class PDFView;
+
+@interface PDFDocument (PDFDocumentDetails)
+- (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate;
+@end
+
+extern "C" NSString *_NSPathForSystemFramework(NSString *framework);
+
+// MARK: C UTILITY FUNCTIONS
+
+static void _applicationInfoForMIMEType(NSString *type, NSString **name, NSImage **image)
+{
+ ASSERT(name);
+ ASSERT(image);
+
+ CFURLRef appURL = 0;
+
+ OSStatus error = LSCopyApplicationForMIMEType((CFStringRef)type, kLSRolesAll, &appURL);
+ if (error != noErr)
+ return;
+
+ NSString *appPath = [(NSURL *)appURL path];
+ if (appURL)
+ CFRelease(appURL);
+
+ *image = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
+ [*image setSize:NSMakeSize(16, 16)];
+
+ *name = [[NSFileManager defaultManager] displayNameAtPath:appPath];
+}
+
+// FIXME 4182876: We can eliminate this function in favor if -isEqual: if [PDFSelection isEqual:] is overridden
+// to compare contents.
+static BOOL _PDFSelectionsAreEqual(PDFSelection *selectionA, PDFSelection *selectionB)
+{
+ NSArray *aPages = [selectionA pages];
+ NSArray *bPages = [selectionB pages];
+
+ if (![aPages isEqual:bPages])
+ return NO;
+
+ NSUInteger count = [aPages count];
+ for (NSUInteger i = 0; i < count; ++i) {
+ NSRect aBounds = [selectionA boundsForPage:[aPages objectAtIndex:i]];
+ NSRect bBounds = [selectionB boundsForPage:[bPages objectAtIndex:i]];
+ if (!NSEqualRects(aBounds, bBounds))
+ return NO;
+ }
+
+ return YES;
+}
+
+@interface WKPDFView : NSView
+{
+ PDFViewController* _pdfViewController;
+
+ RetainPtr<NSView> _pdfPreviewView;
+ PDFView *_pdfView;
+ BOOL _ignoreScaleAndDisplayModeAndPageNotifications;
+ BOOL _willUpdatePreferencesSoon;
+}
+
+- (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController;
+- (void)invalidate;
+- (PDFView *)pdfView;
+- (void)setDocument:(PDFDocument *)pdfDocument;
+
+- (void)_applyPDFPreferences;
+- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection;
+@end
+
+@implementation WKPDFView
+
+- (id)initWithFrame:(NSRect)frame PDFViewController:(PDFViewController*)pdfViewController
+{
+ if ((self = [super initWithFrame:frame])) {
+ _pdfViewController = pdfViewController;
+
+ [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+ Class previewViewClass = PDFViewController::pdfPreviewViewClass();
+ ASSERT(previewViewClass);
+
+ _pdfPreviewView.adoptNS([[previewViewClass alloc] initWithFrame:frame]);
+ [_pdfPreviewView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ [self addSubview:_pdfPreviewView.get()];
+
+ _pdfView = [_pdfPreviewView.get() performSelector:@selector(pdfView)];
+ [_pdfView setDelegate:self];
+ }
+
+ return self;
+}
+
+- (void)invalidate
+{
+ _pdfViewController = 0;
+}
+
+- (PDFView *)pdfView
+{
+ return _pdfView;
+}
+
+- (void)setDocument:(PDFDocument *)pdfDocument
+{
+ _ignoreScaleAndDisplayModeAndPageNotifications = YES;
+ [_pdfView setDocument:pdfDocument];
+ [self _applyPDFPreferences];
+ _ignoreScaleAndDisplayModeAndPageNotifications = NO;
+}
+
+- (void)_applyPDFPreferences
+{
+ if (!_pdfViewController)
+ return;
+
+ WebPreferences *preferences = _pdfViewController->page()->pageGroup()->preferences();
+
+ CGFloat scaleFactor = preferences->pdfScaleFactor();
+ if (!scaleFactor)
+ [_pdfView setAutoScales:YES];
+ else {
+ [_pdfView setAutoScales:NO];
+ [_pdfView setScaleFactor:scaleFactor];
+ }
+ [_pdfView setDisplayMode:preferences->pdfDisplayMode()];
+}
+
+- (void)_updatePreferences:(id)ignored
+{
+ _willUpdatePreferencesSoon = NO;
+
+ if (!_pdfViewController)
+ return;
+
+ WebPreferences* preferences = _pdfViewController->page()->pageGroup()->preferences();
+
+ CGFloat scaleFactor = [_pdfView autoScales] ? 0 : [_pdfView scaleFactor];
+ preferences->setPDFScaleFactor(scaleFactor);
+ preferences->setPDFDisplayMode([_pdfView displayMode]);
+}
+
+- (void)_updatePreferencesSoon
+{
+ if (_willUpdatePreferencesSoon)
+ return;
+
+ [self performSelector:@selector(_updatePreferences:) withObject:nil afterDelay:0];
+ _willUpdatePreferencesSoon = YES;
+}
+
+- (void)_scaleOrDisplayModeOrPageChanged:(NSNotification *)notification
+{
+ ASSERT_ARG(notification, [notification object] == _pdfView);
+ if (!_ignoreScaleAndDisplayModeAndPageNotifications)
+ [self _updatePreferencesSoon];
+}
+
+- (void)_openWithFinder:(id)sender
+{
+ _pdfViewController->openPDFInFinder();
+}
+
+- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection
+{
+ if (![string length])
+ return nil;
+
+ int options = 0;
+ if (!forward)
+ options |= NSBackwardsSearch;
+
+ if (!caseFlag)
+ options |= NSCaseInsensitiveSearch;
+
+ PDFDocument *document = [_pdfView document];
+
+ PDFSelection *selectionForInitialSearch = [initialSelection copy];
+ if (startInSelection) {
+ // Initially we want to include the selected text in the search. So we must modify the starting search
+ // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before
+ // the current selection (if searching forwards) or after (if searching backwards).
+ int initialSelectionLength = [[initialSelection string] length];
+ if (forward) {
+ [selectionForInitialSearch extendSelectionAtStart:1];
+ [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
+ } else {
+ [selectionForInitialSearch extendSelectionAtEnd:1];
+ [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
+ }
+ }
+ PDFSelection *foundSelection = [document findString:string fromSelection:selectionForInitialSearch withOptions:options];
+ [selectionForInitialSearch release];
+
+ // If we first searched in the selection, and we found the selection, search again from just past the selection
+ if (startInSelection && _PDFSelectionsAreEqual(foundSelection, initialSelection))
+ foundSelection = [document findString:string fromSelection:initialSelection withOptions:options];
+
+ if (!foundSelection && wrapFlag)
+ foundSelection = [document findString:string fromSelection:nil withOptions:options];
+
+ return foundSelection;
+}
+
+- (NSUInteger)_countMatches:(NSString *)string caseSensitive:(BOOL)caseFlag
+{
+ if (![string length])
+ return 0;
+
+ int options = caseFlag ? 0 : NSCaseInsensitiveSearch;
+
+ return [[[_pdfView document] findString:string withOptions:options] count];
+}
+
+// MARK: NSView overrides
+
+- (void)viewDidMoveToWindow
+{
+ if (![self window])
+ return;
+
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+ [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewScaleChangedNotification object:_pdfView];
+ [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView];
+ [notificationCenter addObserver:self selector:@selector(_scaleOrDisplayModeOrPageChanged:) name:_webkit_PDFViewPageChangedNotification object:_pdfView];
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)newWindow
+{
+ if (![self window])
+ return;
+
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+ [notificationCenter removeObserver:self name:_webkit_PDFViewScaleChangedNotification object:_pdfView];
+ [notificationCenter removeObserver:self name:_webkit_PDFViewDisplayModeChangedNotification object:_pdfView];
+ [notificationCenter removeObserver:self name:_webkit_PDFViewPageChangedNotification object:_pdfView];
+}
+
+- (NSView *)hitTest:(NSPoint)point
+{
+ // Override hitTest so we can override menuForEvent.
+ NSEvent *event = [NSApp currentEvent];
+ NSEventType type = [event type];
+ if (type == NSRightMouseDown || (type == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask)))
+ return self;
+
+ return [super hitTest:point];
+}
+
+static void insertOpenWithDefaultPDFMenuItem(NSMenu *menu, NSUInteger index)
+{
+ // Add in an "Open with <default PDF viewer>" item
+ NSString *appName = nil;
+ NSImage *appIcon = nil;
+
+ _applicationInfoForMIMEType(@"application/pdf", &appName, &appIcon);
+ if (!appName)
+ appName = WEB_UI_STRING("Finder", "Default application name for Open With context menu");
+
+ // To match the PDFKit style, we'll add Open with Preview even when there's no document yet to view, and
+ // disable it using validateUserInterfaceItem.
+ NSString *title = [NSString stringWithFormat:WEB_UI_STRING("Open with %@", "context menu item for PDF"), appName];
+
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title action:@selector(_openWithFinder:) keyEquivalent:@""];
+ if (appIcon)
+ [item setImage:appIcon];
+ [menu insertItem:item atIndex:index];
+ [item release];
+}
+
+- (NSMenu *)menuForEvent:(NSEvent *)theEvent
+{
+ NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
+
+ bool insertedOpenWithItem = false;
+
+ NSEnumerator *menuItemEnumerator = [[[_pdfView menuForEvent:theEvent] itemArray] objectEnumerator];
+ while (NSMenuItem *item = [menuItemEnumerator nextObject]) {
+ NSMenuItem *itemCopy = [item copy];
+ [menu addItem:itemCopy];
+ [itemCopy release];
+
+ if (insertedOpenWithItem)
+ continue;
+
+ // If a "Copy" item is present, place the "Open With" item just after it, with an intervening separator.
+ if ([item action] != @selector(copy:))
+ continue;
+
+ [menu addItem:[NSMenuItem separatorItem]];
+ insertOpenWithDefaultPDFMenuItem(menu, [menu numberOfItems]);
+ insertedOpenWithItem = true;
+ }
+
+ if (!insertedOpenWithItem) {
+ // No "Copy" item was found; place the "Open With" item at the top of the menu, with a trailing separator.
+ insertOpenWithDefaultPDFMenuItem(menu, 0);
+ [menu insertItem:[NSMenuItem separatorItem] atIndex:1];
+ }
+
+ return [menu autorelease];
+}
+
+// MARK: NSUserInterfaceValidations PROTOCOL IMPLEMENTATION
+
+- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
+{
+ SEL action = [item action];
+ if (action == @selector(_openWithFinder:))
+ return [_pdfView document] != nil;
+ return YES;
+}
+
+// MARK: PDFView delegate methods
+
+- (void)PDFViewWillClickOnLink:(PDFView *)sender withURL:(NSURL *)URL
+{
+ _pdfViewController->linkClicked([URL absoluteString]);
+}
+
+- (void)PDFViewOpenPDFInNativeApplication:(PDFView *)sender
+{
+ _pdfViewController->openPDFInFinder();
+}
+
+- (void)PDFViewSavePDFToDownloadFolder:(PDFView *)sender
+{
+ _pdfViewController->savePDFToDownloadsFolder();
+}
+
+- (void)PDFViewPerformPrint:(PDFView *)sender
+{
+ _pdfViewController->print();
+}
+
+@end
+
+namespace WebKit {
+
+PassOwnPtr<PDFViewController> PDFViewController::create(WKView *wkView)
+{
+ return adoptPtr(new PDFViewController(wkView));
+}
+
+PDFViewController::PDFViewController(WKView *wkView)
+ : m_wkView(wkView)
+ , m_wkPDFView(AdoptNS, [[WKPDFView alloc] initWithFrame:[m_wkView bounds] PDFViewController:this])
+ , m_pdfView([m_wkPDFView.get() pdfView])
+ , m_hasWrittenPDFToDisk(false)
+{
+ [m_wkView addSubview:m_wkPDFView.get()];
+}
+
+PDFViewController::~PDFViewController()
+{
+ [m_wkPDFView.get() removeFromSuperview];
+ [m_wkPDFView.get() invalidate];
+ m_wkPDFView = nullptr;
+}
+
+WebPageProxy* PDFViewController::page() const
+{
+ return toImpl([m_wkView pageRef]);
+}
+
+NSView* PDFViewController::pdfView() const
+{
+ return m_wkPDFView.get();
+}
+
+static RetainPtr<CFDataRef> convertPostScriptDataSourceToPDF(const CoreIPC::DataReference& dataReference)
+{
+ // Convert PostScript to PDF using Quartz 2D API
+ // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html
+
+ CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ RetainPtr<CGPSConverterRef> converter(AdoptCF, CGPSConverterCreate(0, &callbacks, 0));
+ ASSERT(converter);
+
+ RetainPtr<NSData> nsData(AdoptNS, [[NSData alloc] initWithBytesNoCopy:const_cast<uint8_t*>(dataReference.data()) length:dataReference.size() freeWhenDone:NO]);
+
+ RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData((CFDataRef)nsData.get()));
+ ASSERT(provider);
+
+ RetainPtr<CFMutableDataRef> result(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
+ ASSERT(result);
+
+ RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreateWithCFData(result.get()));
+ ASSERT(consumer);
+
+ CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0);
+
+ if (!result)
+ return 0;
+
+ return result;
+}
+
+void PDFViewController::setPDFDocumentData(const String& mimeType, const String& suggestedFilename, const CoreIPC::DataReference& dataReference)
+{
+ if (equalIgnoringCase(mimeType, "application/postscript")) {
+ m_pdfData = convertPostScriptDataSourceToPDF(dataReference);
+ if (!m_pdfData)
+ return;
+ m_suggestedFilename = String(suggestedFilename + ".pdf");
+ } else {
+ // Make sure to copy the data.
+ m_pdfData.adoptCF(CFDataCreate(0, dataReference.data(), dataReference.size()));
+ m_suggestedFilename = suggestedFilename;
+ }
+
+ RetainPtr<PDFDocument> pdfDocument(AdoptNS, [[pdfDocumentClass() alloc] initWithData:(NSData *)m_pdfData.get()]);
+ [m_wkPDFView.get() setDocument:pdfDocument.get()];
+}
+
+double PDFViewController::zoomFactor() const
+{
+ return [m_pdfView scaleFactor];
+}
+
+void PDFViewController::setZoomFactor(double zoomFactor)
+{
+ [m_pdfView setScaleFactor:zoomFactor];
+}
+
+Class PDFViewController::pdfDocumentClass()
+{
+ static Class pdfDocumentClass = [pdfKitBundle() classNamed:@"PDFDocument"];
+
+ return pdfDocumentClass;
+}
+
+Class PDFViewController::pdfPreviewViewClass()
+{
+ static Class pdfPreviewViewClass = [pdfKitBundle() classNamed:@"PDFPreviewView"];
+
+ return pdfPreviewViewClass;
+}
+
+NSBundle* PDFViewController::pdfKitBundle()
+{
+ static NSBundle *pdfKitBundle;
+ if (pdfKitBundle)
+ return pdfKitBundle;
+
+ NSString *pdfKitPath = [_NSPathForSystemFramework(@"Quartz.framework") stringByAppendingString:@"/Frameworks/PDFKit.framework"];
+ if (!pdfKitPath) {
+ LOG_ERROR("Couldn't find PDFKit.framework");
+ return nil;
+ }
+
+ pdfKitBundle = [NSBundle bundleWithPath:pdfKitPath];
+ if (![pdfKitBundle load])
+ LOG_ERROR("Couldn't load PDFKit.framework");
+ return pdfKitBundle;
+}
+
+NSPrintOperation *PDFViewController::makePrintOperation(NSPrintInfo *printInfo)
+{
+ return [[m_pdfView document] getPrintOperationForPrintInfo:printInfo autoRotate:YES];
+}
+
+void PDFViewController::openPDFInFinder()
+{
+ // We don't want to open the PDF until we have a document to write. (see 4892525).
+ if (![m_pdfView document]) {
+ NSBeep();
+ return;
+ }
+
+ NSString *path = pathToPDFOnDisk();
+ if (!path)
+ return;
+
+ if (!m_hasWrittenPDFToDisk) {
+ // Create a PDF file with the minimal permissions (only accessible to the current user, see 4145714).
+ RetainPtr<NSNumber> permissions(AdoptNS, [[NSNumber alloc] initWithInt:S_IRUSR]);
+ RetainPtr<NSDictionary> fileAttributes(AdoptNS, [[NSDictionary alloc] initWithObjectsAndKeys:permissions.get(), NSFilePosixPermissions, nil]);
+
+ if (![[NSFileManager defaultManager] createFileAtPath:path contents:(NSData *)m_pdfData.get() attributes:fileAttributes.get()])
+ return;
+
+ m_hasWrittenPDFToDisk = true;
+ }
+
+ [[NSWorkspace sharedWorkspace] openFile:path];
+}
+
+static void releaseCFData(unsigned char*, const void* data)
+{
+ ASSERT(CFGetTypeID(data) == CFDataGetTypeID());
+
+ // Balanced by CFRetain in savePDFToDownloadsFolder.
+ CFRelease(data);
+}
+
+void PDFViewController::savePDFToDownloadsFolder()
+{
+ // We don't want to write the file until we have a document to write. (see 5267607).
+ if (![m_pdfView document]) {
+ NSBeep();
+ return;
+ }
+
+ ASSERT(m_pdfData);
+
+ // Balanced by CFRelease in releaseCFData.
+ CFRetain(m_pdfData.get());
+
+ RefPtr<WebData> data = WebData::createWithoutCopying(CFDataGetBytePtr(m_pdfData.get()), CFDataGetLength(m_pdfData.get()), releaseCFData, m_pdfData.get());
+
+ page()->saveDataToFileInDownloadsFolder(m_suggestedFilename.get(), page()->mainFrame()->mimeType(), page()->mainFrame()->url(), data.get());
+}
+
+static NSString *temporaryPDFDirectoryPath()
+{
+ static NSString *temporaryPDFDirectoryPath;
+
+ if (!temporaryPDFDirectoryPath) {
+ NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPDFs-XXXXXX"];
+ CString templateRepresentation = [temporaryDirectoryTemplate fileSystemRepresentation];
+
+ if (mkdtemp(templateRepresentation.mutableData()))
+ temporaryPDFDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:templateRepresentation.data() length:templateRepresentation.length()] copy];
+ }
+
+ return temporaryPDFDirectoryPath;
+}
+
+NSString *PDFViewController::pathToPDFOnDisk()
+{
+ if (m_pathToPDFOnDisk)
+ return m_pathToPDFOnDisk.get();
+
+ NSString *pdfDirectoryPath = temporaryPDFDirectoryPath();
+ if (!pdfDirectoryPath)
+ return nil;
+
+ NSString *path = [pdfDirectoryPath stringByAppendingPathComponent:m_suggestedFilename.get()];
+
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ if ([fileManager fileExistsAtPath:path]) {
+ NSString *pathTemplatePrefix = [pdfDirectoryPath stringByAppendingPathComponent:@"XXXXXX-"];
+ NSString *pathTemplate = [pathTemplatePrefix stringByAppendingString:m_suggestedFilename.get()];
+ CString pathTemplateRepresentation = [pathTemplate fileSystemRepresentation];
+
+ int fd = mkstemps(pathTemplateRepresentation.mutableData(), pathTemplateRepresentation.length() - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1);
+ if (fd < 0)
+ return nil;
+
+ close(fd);
+ path = [fileManager stringWithFileSystemRepresentation:pathTemplateRepresentation.data() length:pathTemplateRepresentation.length()];
+ }
+
+ m_pathToPDFOnDisk.adoptNS([path copy]);
+ return path;
+}
+
+void PDFViewController::linkClicked(const String& url)
+{
+ NSEvent* nsEvent = [NSApp currentEvent];
+ WebMouseEvent event;
+ switch ([nsEvent type]) {
+ case NSLeftMouseUp:
+ case NSRightMouseUp:
+ case NSOtherMouseUp:
+ event = WebEventFactory::createWebMouseEvent(nsEvent, m_pdfView);
+ default:
+ // For non mouse-clicks or for keyboard events, pass an empty WebMouseEvent
+ // through. The event is only used by the WebFrameLoaderClient to determine
+ // the modifier keys and which mouse button is down. These queries will be
+ // valid with an empty event.
+ break;
+ }
+
+ page()->linkClicked(url, event);
+}
+
+void PDFViewController::print()
+{
+ page()->printMainFrame();
+}
+
+void PDFViewController::findString(const String& string, FindOptions options, unsigned maxMatchCount)
+{
+ BOOL forward = !(options & FindOptionsBackwards);
+ BOOL caseFlag = !(options & FindOptionsCaseInsensitive);
+ BOOL wrapFlag = options & FindOptionsWrapAround;
+
+ PDFSelection *selection = [m_wkPDFView.get() _nextMatchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag fromSelection:[m_pdfView currentSelection] startInSelection:NO];
+ if (!selection) {
+ page()->didFailToFindString(string);
+ return;
+ }
+
+ NSUInteger matchCount;
+ if (!maxMatchCount) {
+ // If the max was zero, any result means we exceeded the max. We can skip computing the actual count.
+ matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
+ } else {
+ matchCount = [m_wkPDFView.get() _countMatches:string caseSensitive:caseFlag];
+ if (matchCount > maxMatchCount)
+ matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
+ }
+
+ [m_pdfView setCurrentSelection:selection];
+ [m_pdfView scrollSelectionToVisible:nil];
+ page()->didFindString(string, matchCount);
+}
+
+void PDFViewController::countStringMatches(const String& string, FindOptions options, unsigned maxMatchCount)
+{
+ BOOL caseFlag = !(options & FindOptionsCaseInsensitive);
+
+ NSUInteger matchCount = [m_wkPDFView.get() _countMatches:string caseSensitive:caseFlag];
+ if (matchCount > maxMatchCount)
+ matchCount = maxMatchCount;
+ page()->didCountStringMatches(string, matchCount);
+}
+
+} // namespace WebKit