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