1/* 2 * Copyright (C) 2009, 2011, 2012 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 "PDFPlugin.h" 28 29#if ENABLE(PDFKIT_PLUGIN) 30 31#import "ArgumentCoders.h" 32#import "AttributedString.h" 33#import "DataReference.h" 34#import "DictionaryPopupInfo.h" 35#import "PDFAnnotationTextWidgetDetails.h" 36#import "PDFKitImports.h" 37#import "PDFLayerControllerDetails.h" 38#import "PDFPluginAnnotation.h" 39#import "PDFPluginPasswordField.h" 40#import "PluginView.h" 41#import "WKAccessibilityWebPageObjectMac.h" 42#import "WKPageFindMatchesClient.h" 43#import "WebContextMessages.h" 44#import "WebCoreArgumentCoders.h" 45#import "WebEvent.h" 46#import "WebEventConversion.h" 47#import "WebPage.h" 48#import "WebPageProxyMessages.h" 49#import "WebProcess.h" 50#import <JavaScriptCore/JSContextRef.h> 51#import <JavaScriptCore/JSObjectRef.h> 52#import <JavaScriptCore/JSStringRef.h> 53#import <JavaScriptCore/JSStringRefCF.h> 54#import <PDFKit/PDFKit.h> 55#import <QuartzCore/QuartzCore.h> 56#import <WebCore/ArchiveResource.h> 57#import <WebCore/Chrome.h> 58#import <WebCore/Cursor.h> 59#import <WebCore/DocumentLoader.h> 60#import <WebCore/FocusController.h> 61#import <WebCore/FormState.h> 62#import <WebCore/Frame.h> 63#import <WebCore/FrameLoader.h> 64#import <WebCore/FrameView.h> 65#import <WebCore/GraphicsContext.h> 66#import <WebCore/HTMLElement.h> 67#import <WebCore/HTMLFormElement.h> 68#import <WebCore/LocalizedStrings.h> 69#import <WebCore/MouseEvent.h> 70#import <WebCore/Page.h> 71#import <WebCore/Pasteboard.h> 72#import <WebCore/PluginData.h> 73#import <WebCore/PluginDocument.h> 74#import <WebCore/RenderBoxModelObject.h> 75#import <WebCore/ScrollbarTheme.h> 76#import <WebCore/Settings.h> 77#import <WebCore/UUID.h> 78#import <WebKitSystemInterface.h> 79#import <wtf/CurrentTime.h> 80 81using namespace WebCore; 82 83// Set overflow: hidden on the annotation container so <input> elements scrolled out of view don't show 84// scrollbars on the body. We can't add annotations directly to the body, because overflow: hidden on the body 85// will break rubber-banding. 86static const char* annotationStyle = 87"#annotationContainer {" 88" overflow: hidden; " 89" position: absolute; " 90" pointer-events: none; " 91" top: 0; " 92" left: 0; " 93" right: 0; " 94" bottom: 0; " 95" display: -webkit-box; " 96" -webkit-box-align: center; " 97" -webkit-box-pack: center; " 98"} " 99".annotation { " 100" position: absolute; " 101" pointer-events: auto; " 102"} " 103"textarea.annotation { " 104" resize: none; " 105"} " 106"input.annotation[type='password'] { " 107" position: static; " 108" width: 200px; " 109" margin-top: 100px; " 110"} "; 111 112// In non-continuous modes, a single scroll event with a magnitude of >= 20px 113// will jump to the next or previous page, to match PDFKit behavior. 114static const int defaultScrollMagnitudeThresholdForPageFlip = 20; 115 116@interface WKPDFPluginAccessibilityObject : NSObject { 117 PDFLayerController *_pdfLayerController; 118 NSObject *_parent; 119 WebKit::PDFPlugin* _pdfPlugin; 120} 121 122@property (assign) PDFLayerController *pdfLayerController; 123@property (assign) NSObject *parent; 124@property (assign) WebKit::PDFPlugin* pdfPlugin; 125 126- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin; 127 128@end 129 130@implementation WKPDFPluginAccessibilityObject 131 132@synthesize pdfLayerController=_pdfLayerController; 133@synthesize parent=_parent; 134@synthesize pdfPlugin=_pdfPlugin; 135 136- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin 137{ 138 if (!(self = [super init])) 139 return nil; 140 141 _pdfPlugin = plugin; 142 143 return self; 144} 145 146- (BOOL)accessibilityIsIgnored 147{ 148 return NO; 149} 150 151- (id)accessibilityAttributeValue:(NSString *)attribute 152{ 153 if ([attribute isEqualToString:NSAccessibilityParentAttribute]) 154 return _parent; 155 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) 156 return [_pdfLayerController accessibilityValueAttribute]; 157 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) 158 return [_pdfLayerController accessibilitySelectedTextAttribute]; 159 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) 160 return [_pdfLayerController accessibilitySelectedTextRangeAttribute]; 161 if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute]) 162 return [_pdfLayerController accessibilityNumberOfCharactersAttribute]; 163 if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) 164 return [_pdfLayerController accessibilityVisibleCharacterRangeAttribute]; 165 if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) 166 return [_parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; 167 if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 168 return [_pdfLayerController accessibilityRoleAttribute]; 169 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) 170 return [_pdfLayerController accessibilityRoleDescriptionAttribute]; 171 if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) 172 return [_parent accessibilityAttributeValue:NSAccessibilityWindowAttribute]; 173 if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) 174 return [NSValue valueWithSize:_pdfPlugin->boundsOnScreen().size()]; 175 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) 176 return [_parent accessibilityAttributeValue:NSAccessibilityFocusedAttribute]; 177 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) 178 return [_parent accessibilityAttributeValue:NSAccessibilityEnabledAttribute]; 179 if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) 180 return [NSValue valueWithPoint:_pdfPlugin->boundsOnScreen().location()]; 181 182 return 0; 183} 184 185- (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter 186{ 187 if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) { 188 NSRect boundsInPDFViewCoordinates = [[_pdfLayerController accessibilityBoundsForRangeAttributeForParameter:parameter] rectValue]; 189 NSRect boundsInScreenCoordinates = _pdfPlugin->convertFromPDFViewToScreen(boundsInPDFViewCoordinates); 190 return [NSValue valueWithRect:boundsInScreenCoordinates]; 191 } 192 193 if ([attribute isEqualToString:NSAccessibilityLineForIndexParameterizedAttribute]) 194 return [_pdfLayerController accessibilityLineForIndexAttributeForParameter:parameter]; 195 if ([attribute isEqualToString:NSAccessibilityRangeForLineParameterizedAttribute]) 196 return [_pdfLayerController accessibilityRangeForLineAttributeForParameter:parameter]; 197 if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) 198 return [_pdfLayerController accessibilityStringForRangeAttributeForParameter:parameter]; 199 200 return 0; 201} 202 203- (CPReadingModel *)readingModel 204{ 205 return [_pdfLayerController readingModel]; 206} 207 208- (NSArray *)accessibilityAttributeNames 209{ 210 static NSArray *attributeNames = 0; 211 212 if (!attributeNames) { 213 attributeNames = @[NSAccessibilityValueAttribute, 214 NSAccessibilitySelectedTextAttribute, 215 NSAccessibilitySelectedTextRangeAttribute, 216 NSAccessibilityNumberOfCharactersAttribute, 217 NSAccessibilityVisibleCharacterRangeAttribute, 218 NSAccessibilityParentAttribute, 219 NSAccessibilityRoleAttribute, 220 NSAccessibilityWindowAttribute, 221 NSAccessibilityTopLevelUIElementAttribute, 222 NSAccessibilityRoleDescriptionAttribute, 223 NSAccessibilitySizeAttribute, 224 NSAccessibilityFocusedAttribute, 225 NSAccessibilityEnabledAttribute, 226 NSAccessibilityPositionAttribute]; 227 [attributeNames retain]; 228 } 229 230 return attributeNames; 231} 232 233- (NSArray *)accessibilityActionNames 234{ 235 static NSArray *actionNames = 0; 236 237 if (!actionNames) 238 actionNames = [[NSArray arrayWithObject:NSAccessibilityShowMenuAction] retain]; 239 240 return actionNames; 241} 242 243- (void)accessibilityPerformAction:(NSString *)action 244{ 245 if ([action isEqualToString:NSAccessibilityShowMenuAction]) 246 _pdfPlugin->showContextMenuAtPoint(IntRect(IntPoint(), _pdfPlugin->size()).center()); 247} 248 249- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute 250{ 251 return [_pdfLayerController accessibilityIsAttributeSettable:attribute]; 252} 253 254- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute 255{ 256 return [_pdfLayerController accessibilitySetValue:value forAttribute:attribute]; 257} 258 259- (NSArray *)accessibilityParameterizedAttributeNames 260{ 261 return [_pdfLayerController accessibilityParameterizedAttributeNames]; 262} 263 264- (id)accessibilityFocusedUIElement 265{ 266 return self; 267} 268 269- (id)accessibilityHitTest:(NSPoint)point 270{ 271 return self; 272} 273 274@end 275 276 277@interface WKPDFPluginScrollbarLayer : CALayer { 278 WebKit::PDFPlugin* _pdfPlugin; 279} 280 281@property (assign) WebKit::PDFPlugin* pdfPlugin; 282 283@end 284 285@implementation WKPDFPluginScrollbarLayer 286 287@synthesize pdfPlugin=_pdfPlugin; 288 289- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin 290{ 291 if (!(self = [super init])) 292 return nil; 293 294 _pdfPlugin = plugin; 295 296 return self; 297} 298 299- (id<CAAction>)actionForKey:(NSString *)key 300{ 301 return nil; 302} 303 304- (void)drawInContext:(CGContextRef)ctx 305{ 306 _pdfPlugin->paintControlForLayerInContext(self, ctx); 307} 308 309@end 310 311@interface WKPDFLayerControllerDelegate : NSObject<PDFLayerControllerDelegate> { 312 WebKit::PDFPlugin* _pdfPlugin; 313} 314 315@property (assign) WebKit::PDFPlugin* pdfPlugin; 316 317@end 318 319@implementation WKPDFLayerControllerDelegate 320 321@synthesize pdfPlugin=_pdfPlugin; 322 323- (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin 324{ 325 if (!(self = [super init])) 326 return nil; 327 328 _pdfPlugin = plugin; 329 330 return self; 331} 332 333- (void)updateScrollPosition:(CGPoint)newPosition 334{ 335 _pdfPlugin->notifyScrollPositionChanged(IntPoint(newPosition)); 336} 337 338- (void)writeItemsToPasteboard:(NSArray *)items withTypes:(NSArray *)types 339{ 340 _pdfPlugin->writeItemsToPasteboard(NSGeneralPboard, items, types); 341} 342 343- (void)showDefinitionForAttributedString:(NSAttributedString *)string atPoint:(CGPoint)point 344{ 345 _pdfPlugin->showDefinitionForAttributedString(string, point); 346} 347 348- (void)performWebSearch:(NSString *)string 349{ 350 _pdfPlugin->performWebSearch(string); 351} 352 353- (void)performSpotlightSearch:(NSString *)string 354{ 355 _pdfPlugin->performSpotlightSearch(string); 356} 357 358- (void)openWithNativeApplication 359{ 360 _pdfPlugin->openWithNativeApplication(); 361} 362 363- (void)saveToPDF 364{ 365 _pdfPlugin->saveToPDF(); 366} 367 368- (void)pdfLayerController:(PDFLayerController *)pdfLayerController clickedLinkWithURL:(NSURL *)url 369{ 370 _pdfPlugin->clickedLink(url); 371} 372 373- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeActiveAnnotation:(PDFAnnotation *)annotation 374{ 375 _pdfPlugin->setActiveAnnotation(annotation); 376} 377 378- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeContentScaleFactor:(CGFloat)scaleFactor 379{ 380 _pdfPlugin->notifyContentScaleFactorChanged(scaleFactor); 381} 382 383- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeDisplayMode:(int)mode 384{ 385 _pdfPlugin->notifyDisplayModeChanged(mode); 386} 387 388- (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeSelection:(PDFSelection *)selection 389{ 390 _pdfPlugin->notifySelectionChanged(selection); 391} 392 393@end 394 395static const char* postScriptMIMEType = "application/postscript"; 396const uint64_t pdfDocumentRequestID = 1; // PluginController supports loading multiple streams, but we only need one for PDF. 397 398static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values) 399{ 400 CGPDFArrayRef names; 401 if (CGPDFDictionaryGetArray(subtree, "Names", &names)) { 402 size_t nameCount = CGPDFArrayGetCount(names) / 2; 403 for (size_t i = 0; i < nameCount; ++i) { 404 CGPDFObjectRef object; 405 CGPDFArrayGetObject(names, 2 * i + 1, &object); 406 values.append(object); 407 } 408 return; 409 } 410 411 CGPDFArrayRef kids; 412 if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids)) 413 return; 414 415 size_t kidCount = CGPDFArrayGetCount(kids); 416 for (size_t i = 0; i < kidCount; ++i) { 417 CGPDFDictionaryRef kid; 418 if (!CGPDFArrayGetDictionary(kids, i, &kid)) 419 continue; 420 appendValuesInPDFNameSubtreeToVector(kid, values); 421 } 422} 423 424static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues) 425{ 426 appendValuesInPDFNameSubtreeToVector(tree, allValues); 427} 428 429static void getAllScriptsInPDFDocument(CGPDFDocumentRef pdfDocument, Vector<RetainPtr<CFStringRef>>& scripts) 430{ 431 if (!pdfDocument) 432 return; 433 434 CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument); 435 if (!pdfCatalog) 436 return; 437 438 // Get the dictionary of all document-level name trees. 439 CGPDFDictionaryRef namesDictionary; 440 if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary)) 441 return; 442 443 // Get the document-level "JavaScript" name tree. 444 CGPDFDictionaryRef javaScriptNameTree; 445 if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree)) 446 return; 447 448 // The names are arbitrary. We are only interested in the values. 449 Vector<CGPDFObjectRef> objects; 450 getAllValuesInPDFNameTree(javaScriptNameTree, objects); 451 size_t objectCount = objects.size(); 452 453 for (size_t i = 0; i < objectCount; ++i) { 454 CGPDFDictionaryRef javaScriptAction; 455 if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction)) 456 continue; 457 458 // A JavaScript action must have an action type of "JavaScript". 459 const char* actionType; 460 if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript")) 461 continue; 462 463 const UInt8* bytes = 0; 464 CFIndex length; 465 CGPDFStreamRef stream; 466 CGPDFStringRef string; 467 RetainPtr<CFDataRef> data; 468 if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) { 469 CGPDFDataFormat format; 470 data = adoptCF(CGPDFStreamCopyData(stream, &format)); 471 if (!data) 472 continue; 473 bytes = CFDataGetBytePtr(data.get()); 474 length = CFDataGetLength(data.get()); 475 } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) { 476 bytes = CGPDFStringGetBytePtr(string); 477 length = CGPDFStringGetLength(string); 478 } 479 if (!bytes) 480 continue; 481 482 CFStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? kCFStringEncodingUnicode : kCFStringEncodingUTF8; 483 RetainPtr<CFStringRef> script = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, bytes, length, encoding, true)); 484 if (!script) 485 continue; 486 487 scripts.append(script); 488 } 489} 490 491namespace WebKit { 492 493using namespace HTMLNames; 494 495PassRefPtr<PDFPlugin> PDFPlugin::create(WebFrame* frame) 496{ 497 return adoptRef(new PDFPlugin(frame)); 498} 499 500PDFPlugin::PDFPlugin(WebFrame* frame) 501 : m_frame(frame) 502 , m_isPostScript(false) 503 , m_pdfDocumentWasMutated(false) 504 , m_containerLayer(adoptNS([[CALayer alloc] init])) 505 , m_contentLayer(adoptNS([[CALayer alloc] init])) 506 , m_scrollCornerLayer(adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this])) 507 , m_pdfLayerController(adoptNS([[pdfLayerControllerClass() alloc] init])) 508 , m_pdfLayerControllerDelegate(adoptNS([[WKPDFLayerControllerDelegate alloc] initWithPDFPlugin:this])) 509{ 510 m_pdfLayerController.get().delegate = m_pdfLayerControllerDelegate.get(); 511 m_pdfLayerController.get().parentLayer = m_contentLayer.get(); 512 513 if (supportsForms()) { 514 Document* document = webFrame()->coreFrame()->document(); 515 m_annotationContainer = document->createElement(divTag, false); 516 m_annotationContainer->setAttribute(idAttr, "annotationContainer"); 517 518 RefPtr<Element> m_annotationStyle = document->createElement(styleTag, false); 519 m_annotationStyle->setTextContent(annotationStyle, ASSERT_NO_EXCEPTION); 520 521 m_annotationContainer->appendChild(m_annotationStyle.get()); 522 document->body()->appendChild(m_annotationContainer.get()); 523 } 524 525 m_accessibilityObject = adoptNS([[WKPDFPluginAccessibilityObject alloc] initWithPDFPlugin:this]); 526 m_accessibilityObject.get().pdfLayerController = m_pdfLayerController.get(); 527 m_accessibilityObject.get().parent = webFrame()->page()->accessibilityRemoteObject(); 528 529 [m_containerLayer addSublayer:m_contentLayer.get()]; 530 [m_containerLayer addSublayer:m_scrollCornerLayer.get()]; 531} 532 533PDFPlugin::~PDFPlugin() 534{ 535} 536 537PluginInfo PDFPlugin::pluginInfo() 538{ 539 PluginInfo info; 540 info.name = builtInPDFPluginName(); 541 info.isApplicationPlugin = true; 542 543 MimeClassInfo pdfMimeClassInfo; 544 pdfMimeClassInfo.type = "application/pdf"; 545 pdfMimeClassInfo.desc = pdfDocumentTypeDescription(); 546 pdfMimeClassInfo.extensions.append("pdf"); 547 info.mimes.append(pdfMimeClassInfo); 548 549 MimeClassInfo textPDFMimeClassInfo; 550 textPDFMimeClassInfo.type = "text/pdf"; 551 textPDFMimeClassInfo.desc = pdfDocumentTypeDescription(); 552 textPDFMimeClassInfo.extensions.append("pdf"); 553 info.mimes.append(textPDFMimeClassInfo); 554 555 MimeClassInfo postScriptMimeClassInfo; 556 postScriptMimeClassInfo.type = postScriptMIMEType; 557 postScriptMimeClassInfo.desc = postScriptDocumentTypeDescription(); 558 postScriptMimeClassInfo.extensions.append("ps"); 559 info.mimes.append(postScriptMimeClassInfo); 560 561 return info; 562} 563 564void PDFPlugin::updateScrollbars() 565{ 566 bool hadScrollbars = m_horizontalScrollbar || m_verticalScrollbar; 567 568 if (m_horizontalScrollbar) { 569 if (m_size.width() >= m_pdfDocumentSize.width()) 570 destroyScrollbar(HorizontalScrollbar); 571 } else if (m_size.width() < m_pdfDocumentSize.width()) 572 m_horizontalScrollbar = createScrollbar(HorizontalScrollbar); 573 574 if (m_verticalScrollbar) { 575 if (m_size.height() >= m_pdfDocumentSize.height()) 576 destroyScrollbar(VerticalScrollbar); 577 } else if (m_size.height() < m_pdfDocumentSize.height()) 578 m_verticalScrollbar = createScrollbar(VerticalScrollbar); 579 580 int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0; 581 int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0; 582 583 int pageStep = m_pageBoxes.isEmpty() ? 0 : m_pageBoxes[0].height(); 584 585 if (m_horizontalScrollbar) { 586 m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); 587 m_horizontalScrollbar->setProportion(m_size.width() - verticalScrollbarWidth, m_pdfDocumentSize.width()); 588 IntRect scrollbarRect(pluginView()->x(), pluginView()->y() + m_size.height() - m_horizontalScrollbar->height(), m_size.width(), m_horizontalScrollbar->height()); 589 if (m_verticalScrollbar) 590 scrollbarRect.contract(m_verticalScrollbar->width(), 0); 591 m_horizontalScrollbar->setFrameRect(scrollbarRect); 592 } 593 if (m_verticalScrollbar) { 594 m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); 595 m_verticalScrollbar->setProportion(m_size.height() - horizontalScrollbarHeight, m_pdfDocumentSize.height()); 596 IntRect scrollbarRect(IntRect(pluginView()->x() + m_size.width() - m_verticalScrollbar->width(), pluginView()->y(), m_verticalScrollbar->width(), m_size.height())); 597 if (m_horizontalScrollbar) 598 scrollbarRect.contract(0, m_horizontalScrollbar->height()); 599 m_verticalScrollbar->setFrameRect(scrollbarRect); 600 } 601 602 FrameView* frameView = m_frame->coreFrame()->view(); 603 if (!frameView) 604 return; 605 606 bool hasScrollbars = m_horizontalScrollbar || m_verticalScrollbar; 607 if (hadScrollbars != hasScrollbars) { 608 if (hasScrollbars) 609 frameView->addScrollableArea(this); 610 else 611 frameView->removeScrollableArea(this); 612 613 frameView->setNeedsLayout(); 614 } 615 616 if (m_verticalScrollbarLayer) { 617 m_verticalScrollbarLayer.get().frame = verticalScrollbar()->frameRect(); 618 [m_verticalScrollbarLayer setNeedsDisplay]; 619 } 620 621 if (m_horizontalScrollbarLayer) { 622 m_horizontalScrollbarLayer.get().frame = horizontalScrollbar()->frameRect(); 623 [m_horizontalScrollbarLayer setNeedsDisplay]; 624 } 625 626 if (m_scrollCornerLayer) { 627 m_scrollCornerLayer.get().frame = scrollCornerRect(); 628 [m_scrollCornerLayer setNeedsDisplay]; 629 } 630} 631 632PluginView* PDFPlugin::pluginView() 633{ 634 return static_cast<PluginView*>(controller()); 635} 636 637const PluginView* PDFPlugin::pluginView() const 638{ 639 return static_cast<const PluginView*>(controller()); 640} 641 642PassRefPtr<Scrollbar> PDFPlugin::createScrollbar(ScrollbarOrientation orientation) 643{ 644 RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); 645 if (orientation == HorizontalScrollbar) { 646 m_horizontalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]); 647 [m_containerLayer addSublayer:m_horizontalScrollbarLayer.get()]; 648 } else { 649 m_verticalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]); 650 [m_containerLayer addSublayer:m_verticalScrollbarLayer.get()]; 651 } 652 didAddScrollbar(widget.get(), orientation); 653 pluginView()->frame()->view()->addChild(widget.get()); 654 return widget.release(); 655} 656 657void PDFPlugin::destroyScrollbar(ScrollbarOrientation orientation) 658{ 659 RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_horizontalScrollbar : m_verticalScrollbar; 660 if (!scrollbar) 661 return; 662 663 willRemoveScrollbar(scrollbar.get(), orientation); 664 scrollbar->removeFromParent(); 665 scrollbar->disconnectFromScrollableArea(); 666 scrollbar = 0; 667 668 if (orientation == HorizontalScrollbar) { 669 [m_horizontalScrollbarLayer removeFromSuperlayer]; 670 m_horizontalScrollbarLayer = 0; 671 } else { 672 [m_verticalScrollbarLayer removeFromSuperlayer]; 673 m_verticalScrollbarLayer = 0; 674 } 675} 676 677IntRect PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const 678{ 679 IntRect rect = scrollbarRect; 680 rect.move(scrollbar->location() - pluginView()->location()); 681 682 return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->renderer(), rect); 683} 684 685IntRect PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const 686{ 687 IntRect rect = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->renderer(), parentRect); 688 rect.move(pluginView()->location() - scrollbar->location()); 689 690 return rect; 691} 692 693IntPoint PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const 694{ 695 IntPoint point = scrollbarPoint; 696 point.move(scrollbar->location() - pluginView()->location()); 697 698 return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->renderer(), point); 699} 700 701IntPoint PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const 702{ 703 IntPoint point = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->renderer(), parentPoint); 704 point.move(pluginView()->location() - scrollbar->location()); 705 706 return point; 707} 708 709bool PDFPlugin::handleScroll(ScrollDirection direction, ScrollGranularity granularity) 710{ 711 return scroll(direction, granularity); 712} 713 714IntRect PDFPlugin::scrollCornerRect() const 715{ 716 if (!m_horizontalScrollbar || !m_verticalScrollbar) 717 return IntRect(); 718 if (m_horizontalScrollbar->isOverlayScrollbar()) { 719 ASSERT(m_verticalScrollbar->isOverlayScrollbar()); 720 return IntRect(); 721 } 722 return IntRect(pluginView()->width() - m_verticalScrollbar->width(), pluginView()->height() - m_horizontalScrollbar->height(), m_verticalScrollbar->width(), m_horizontalScrollbar->height()); 723} 724 725ScrollableArea* PDFPlugin::enclosingScrollableArea() const 726{ 727 // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer. 728 return 0; 729} 730 731IntRect PDFPlugin::scrollableAreaBoundingBox() const 732{ 733 return pluginView()->frameRect(); 734} 735 736int PDFPlugin::scrollSize(ScrollbarOrientation orientation) const 737{ 738 Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get(); 739 return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0; 740} 741 742bool PDFPlugin::isActive() const 743{ 744 if (Frame* coreFrame = m_frame->coreFrame()) { 745 if (Page* page = coreFrame->page()) 746 return page->focusController().isActive(); 747 } 748 749 return false; 750} 751 752bool PDFPlugin::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const 753{ 754 if (Frame* coreFrame = m_frame->coreFrame()) { 755 if (Page* page = coreFrame->page()) 756 return page->settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting(); 757 } 758 759 return false; 760} 761 762int PDFPlugin::scrollPosition(Scrollbar* scrollbar) const 763{ 764 if (scrollbar->orientation() == HorizontalScrollbar) 765 return m_scrollOffset.width(); 766 if (scrollbar->orientation() == VerticalScrollbar) 767 return m_scrollOffset.height(); 768 ASSERT_NOT_REACHED(); 769 return 0; 770} 771 772IntPoint PDFPlugin::scrollPosition() const 773{ 774 return IntPoint(m_scrollOffset.width(), m_scrollOffset.height()); 775} 776 777IntPoint PDFPlugin::minimumScrollPosition() const 778{ 779 return IntPoint(); 780} 781 782IntPoint PDFPlugin::maximumScrollPosition() const 783{ 784 int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0; 785 int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0; 786 787 IntPoint maximumOffset(m_pdfDocumentSize.width() - m_size.width() + verticalScrollbarWidth, m_pdfDocumentSize.height() - m_size.height() + horizontalScrollbarHeight); 788 maximumOffset.clampNegativeToZero(); 789 return maximumOffset; 790} 791 792void PDFPlugin::scrollbarStyleChanged(int, bool forceUpdate) 793{ 794 if (!forceUpdate) 795 return; 796 797 // If the PDF was scrolled all the way to bottom right and scrollbars change to overlay style, we don't want to display white rectangles where scrollbars were. 798 IntPoint newScrollOffset = IntPoint(m_scrollOffset).shrunkTo(maximumScrollPosition()); 799 setScrollOffset(newScrollOffset); 800 801 // As size of the content area changes, scrollbars may need to appear or to disappear. 802 updateScrollbars(); 803 804 ScrollableArea::contentsResized(); 805} 806 807void PDFPlugin::addArchiveResource() 808{ 809 // FIXME: It's a hack to force add a resource to DocumentLoader. PDF documents should just be fetched as CachedResources. 810 811 // Add just enough data for context menu handling and web archives to work. 812 ResourceResponse synthesizedResponse; 813 synthesizedResponse.setSuggestedFilename(m_suggestedFilename); 814 synthesizedResponse.setURL(m_sourceURL); // Needs to match the HitTestResult::absolutePDFURL. 815 synthesizedResponse.setMimeType("application/pdf"); 816 817 RefPtr<ArchiveResource> resource = ArchiveResource::create(SharedBuffer::wrapCFData(m_data.get()), m_sourceURL, "application/pdf", String(), String(), synthesizedResponse); 818 pluginView()->frame()->document()->loader()->addArchiveResource(resource.release()); 819} 820 821static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object) 822{ 823 PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object)); 824 pdfView->ref(); 825} 826 827static void jsPDFDocFinalize(JSObjectRef object) 828{ 829 PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object)); 830 pdfView->deref(); 831} 832 833JSValueRef PDFPlugin::jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 834{ 835 PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(thisObject)); 836 837 WebFrame* frame = pdfView->m_frame; 838 if (!frame) 839 return JSValueMakeUndefined(ctx); 840 841 Frame* coreFrame = frame->coreFrame(); 842 if (!coreFrame) 843 return JSValueMakeUndefined(ctx); 844 845 Page* page = coreFrame->page(); 846 if (!page) 847 return JSValueMakeUndefined(ctx); 848 849 page->chrome().print(coreFrame); 850 851 return JSValueMakeUndefined(ctx); 852} 853 854JSObjectRef PDFPlugin::makeJSPDFDoc(JSContextRef ctx) 855{ 856 static JSStaticFunction jsPDFDocStaticFunctions[] = { 857 { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 858 { 0, 0, 0 }, 859 }; 860 861 static JSClassDefinition jsPDFDocClassDefinition = { 862 0, 863 kJSClassAttributeNone, 864 "Doc", 865 0, 866 0, 867 jsPDFDocStaticFunctions, 868 jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0 869 }; 870 871 static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition); 872 873 return JSObjectMake(ctx, jsPDFDocClass, this); 874} 875 876static RetainPtr<CFMutableDataRef> convertPostScriptDataToPDF(RetainPtr<CFDataRef> postScriptData) 877{ 878 // Convert PostScript to PDF using the Quartz 2D API. 879 // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html 880 881 CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 }; 882 RetainPtr<CGPSConverterRef> converter = adoptCF(CGPSConverterCreate(0, &callbacks, 0)); 883 RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateWithCFData(postScriptData.get())); 884 RetainPtr<CFMutableDataRef> pdfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0)); 885 RetainPtr<CGDataConsumerRef> consumer = adoptCF(CGDataConsumerCreateWithCFData(pdfData.get())); 886 887 CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0); 888 889 return pdfData; 890} 891 892void PDFPlugin::convertPostScriptDataIfNeeded() 893{ 894 if (!m_isPostScript) 895 return; 896 897 m_suggestedFilename = String(m_suggestedFilename + ".pdf"); 898 m_data = convertPostScriptDataToPDF(m_data); 899} 900 901void PDFPlugin::pdfDocumentDidLoad() 902{ 903 addArchiveResource(); 904 905 RetainPtr<PDFDocument> document = adoptNS([[pdfDocumentClass() alloc] initWithData:rawData()]); 906 907 setPDFDocument(document); 908 909 updatePageAndDeviceScaleFactors(); 910 911 [m_pdfLayerController setFrameSize:size()]; 912 m_pdfLayerController.get().document = document.get(); 913 914 if (handlesPageScaleFactor()) 915 pluginView()->setPageScaleFactor([m_pdfLayerController contentScaleFactor], IntPoint()); 916 917 notifyScrollPositionChanged(IntPoint([m_pdfLayerController scrollPosition])); 918 919 calculateSizes(); 920 updateScrollbars(); 921 922 runScriptsInPDFDocument(); 923 924 if ([document isLocked]) 925 createPasswordEntryForm(); 926} 927 928void PDFPlugin::streamDidReceiveResponse(uint64_t streamID, const URL&, uint32_t, uint32_t, const String& mimeType, const String&, const String& suggestedFilename) 929{ 930 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID); 931 932 m_suggestedFilename = suggestedFilename; 933 934 if (equalIgnoringCase(mimeType, postScriptMIMEType)) 935 m_isPostScript = true; 936} 937 938void PDFPlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length) 939{ 940 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID); 941 942 if (!m_data) 943 m_data = adoptCF(CFDataCreateMutable(0, 0)); 944 945 CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length); 946} 947 948void PDFPlugin::streamDidFinishLoading(uint64_t streamID) 949{ 950 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID); 951 952 convertPostScriptDataIfNeeded(); 953 pdfDocumentDidLoad(); 954} 955 956void PDFPlugin::streamDidFail(uint64_t streamID, bool wasCancelled) 957{ 958 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID); 959 960 m_data.clear(); 961} 962 963void PDFPlugin::manualStreamDidReceiveResponse(const URL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFilename) 964{ 965 m_suggestedFilename = suggestedFilename; 966 967 if (equalIgnoringCase(mimeType, postScriptMIMEType)) 968 m_isPostScript = true; 969} 970 971void PDFPlugin::manualStreamDidReceiveData(const char* bytes, int length) 972{ 973 if (!m_data) 974 m_data = adoptCF(CFDataCreateMutable(0, 0)); 975 976 CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length); 977} 978 979void PDFPlugin::manualStreamDidFinishLoading() 980{ 981 convertPostScriptDataIfNeeded(); 982 pdfDocumentDidLoad(); 983} 984 985void PDFPlugin::manualStreamDidFail(bool) 986{ 987 m_data.clear(); 988} 989 990void PDFPlugin::runScriptsInPDFDocument() 991{ 992 Vector<RetainPtr<CFStringRef>> scripts; 993 getAllScriptsInPDFDocument([m_pdfDocument documentRef], scripts); 994 995 size_t scriptCount = scripts.size(); 996 if (!scriptCount) 997 return; 998 999 JSGlobalContextRef ctx = JSGlobalContextCreate(0); 1000 JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx); 1001 1002 for (size_t i = 0; i < scriptCount; ++i) { 1003 JSStringRef script = JSStringCreateWithCFString(scripts[i].get()); 1004 JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0); 1005 JSStringRelease(script); 1006 } 1007 1008 JSGlobalContextRelease(ctx); 1009} 1010 1011void PDFPlugin::createPasswordEntryForm() 1012{ 1013 m_passwordField = PDFPluginPasswordField::create(m_pdfLayerController.get(), this); 1014 m_passwordField->attach(m_annotationContainer.get()); 1015} 1016 1017void PDFPlugin::attemptToUnlockPDF(const String& password) 1018{ 1019 [m_pdfLayerController attemptToUnlockWithPassword:password]; 1020 1021 if (![pdfDocument() isLocked]) { 1022 m_passwordField = nullptr; 1023 1024 calculateSizes(); 1025 updateScrollbars(); 1026 } 1027} 1028 1029void PDFPlugin::updatePageAndDeviceScaleFactors() 1030{ 1031 double newScaleFactor = controller()->contentsScaleFactor(); 1032 if (!handlesPageScaleFactor()) 1033 newScaleFactor *= webFrame()->page()->pageScaleFactor(); 1034 1035 [m_pdfLayerController setDeviceScaleFactor:newScaleFactor]; 1036} 1037 1038void PDFPlugin::contentsScaleFactorChanged(float) 1039{ 1040 updatePageAndDeviceScaleFactors(); 1041} 1042 1043void PDFPlugin::computePageBoxes() 1044{ 1045 size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument documentRef]); 1046 for (size_t i = 0; i < pageCount; ++i) { 1047 CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument documentRef], i + 1); 1048 ASSERT(pdfPage); 1049 1050 CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox); 1051 if (CGRectIsEmpty(box)) 1052 box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox); 1053 m_pageBoxes.append(IntRect(box)); 1054 } 1055} 1056 1057void PDFPlugin::calculateSizes() 1058{ 1059 if ([pdfDocument() isLocked]) { 1060 setPDFDocumentSize(IntSize(0, 0)); 1061 return; 1062 } 1063 1064 // FIXME: This should come straight from PDFKit. 1065 computePageBoxes(); 1066 1067 setPDFDocumentSize(IntSize([m_pdfLayerController contentSizeRespectingZoom])); 1068} 1069 1070bool PDFPlugin::initialize(const Parameters& parameters) 1071{ 1072 m_sourceURL = parameters.url; 1073 if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty()) 1074 controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false); 1075 1076 controller()->didInitializePlugin(); 1077 return true; 1078} 1079 1080void PDFPlugin::destroy() 1081{ 1082 m_pdfLayerController.get().delegate = 0; 1083 1084 if (webFrame()) { 1085 if (FrameView* frameView = webFrame()->coreFrame()->view()) 1086 frameView->removeScrollableArea(this); 1087 } 1088 1089 m_activeAnnotation = 0; 1090 m_annotationContainer = 0; 1091 1092 destroyScrollbar(HorizontalScrollbar); 1093 destroyScrollbar(VerticalScrollbar); 1094 1095 [m_scrollCornerLayer removeFromSuperlayer]; 1096 [m_contentLayer removeFromSuperlayer]; 1097} 1098 1099void PDFPlugin::updateControlTints(GraphicsContext* graphicsContext) 1100{ 1101 ASSERT(graphicsContext->updatingControlTints()); 1102 1103 if (m_horizontalScrollbar) 1104 m_horizontalScrollbar->invalidate(); 1105 if (m_verticalScrollbar) 1106 m_verticalScrollbar->invalidate(); 1107 invalidateScrollCorner(scrollCornerRect()); 1108} 1109 1110void PDFPlugin::paintControlForLayerInContext(CALayer *layer, CGContextRef context) 1111{ 1112 GraphicsContext graphicsContext(context); 1113 GraphicsContextStateSaver stateSaver(graphicsContext); 1114 1115 graphicsContext.setIsCALayerContext(true); 1116 1117 if (layer == m_scrollCornerLayer) { 1118 IntRect scrollCornerRect = this->scrollCornerRect(); 1119 graphicsContext.translate(-scrollCornerRect.x(), -scrollCornerRect.y()); 1120 ScrollbarTheme::theme()->paintScrollCorner(0, &graphicsContext, scrollCornerRect); 1121 return; 1122 } 1123 1124 Scrollbar* scrollbar = nullptr; 1125 1126 if (layer == m_verticalScrollbarLayer) 1127 scrollbar = verticalScrollbar(); 1128 else if (layer == m_horizontalScrollbarLayer) 1129 scrollbar = horizontalScrollbar(); 1130 1131 if (!scrollbar) 1132 return; 1133 1134 graphicsContext.translate(-scrollbar->x(), -scrollbar->y()); 1135 scrollbar->paint(&graphicsContext, scrollbar->frameRect()); 1136} 1137 1138PassRefPtr<ShareableBitmap> PDFPlugin::snapshot() 1139{ 1140 if (size().isEmpty()) 1141 return nullptr; 1142 1143 float contentsScaleFactor = controller()->contentsScaleFactor(); 1144 IntSize backingStoreSize = size(); 1145 backingStoreSize.scale(contentsScaleFactor); 1146 1147 RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha); 1148 auto context = bitmap->createGraphicsContext(); 1149 1150 context->scale(FloatSize(contentsScaleFactor, -contentsScaleFactor)); 1151 context->translate(-m_scrollOffset.width(), -m_pdfDocumentSize.height() + m_scrollOffset.height()); 1152 1153 [m_pdfLayerController snapshotInContext:context->platformContext()]; 1154 1155 return bitmap.release(); 1156} 1157 1158PlatformLayer* PDFPlugin::pluginLayer() 1159{ 1160 return m_containerLayer.get(); 1161} 1162 1163IntPoint PDFPlugin::convertFromPluginToPDFView(const IntPoint& point) const 1164{ 1165 return IntPoint(point.x(), size().height() - point.y()); 1166} 1167 1168IntPoint PDFPlugin::convertFromRootViewToPlugin(const IntPoint& point) const 1169{ 1170 return m_rootViewToPluginTransform.mapPoint(point); 1171} 1172 1173IntPoint PDFPlugin::convertFromPDFViewToRootView(const IntPoint& point) const 1174{ 1175 IntPoint pointInPluginCoordinates(point.x(), size().height() - point.y()); 1176 return m_rootViewToPluginTransform.inverse().mapPoint(pointInPluginCoordinates); 1177} 1178 1179FloatRect PDFPlugin::convertFromPDFViewToScreen(const FloatRect& rect) const 1180{ 1181 FrameView* frameView = webFrame()->coreFrame()->view(); 1182 1183 if (!frameView) 1184 return FloatRect(); 1185 1186 FloatPoint originInPluginCoordinates(rect.x(), size().height() - rect.y() - rect.height()); 1187 FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(FloatRect(originInPluginCoordinates, rect.size())); 1188 1189 return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates)); 1190} 1191 1192IntRect PDFPlugin::boundsOnScreen() const 1193{ 1194 FrameView* frameView = webFrame()->coreFrame()->view(); 1195 1196 if (!frameView) 1197 return IntRect(); 1198 1199 FloatRect bounds = FloatRect(FloatPoint(), size()); 1200 FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(bounds); 1201 return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates)); 1202} 1203 1204void PDFPlugin::geometryDidChange(const IntSize& pluginSize, const IntRect&, const AffineTransform& pluginToRootViewTransform) 1205{ 1206 if (size() == pluginSize && pluginView()->pageScaleFactor() == [m_pdfLayerController contentScaleFactor]) 1207 return; 1208 1209 m_size = pluginSize; 1210 m_rootViewToPluginTransform = pluginToRootViewTransform.inverse(); 1211 [m_pdfLayerController setFrameSize:pluginSize]; 1212 1213 [CATransaction begin]; 1214 [CATransaction setDisableActions:YES]; 1215 CATransform3D transform = CATransform3DMakeScale(1, -1, 1); 1216 transform = CATransform3DTranslate(transform, 0, -pluginSize.height(), 0); 1217 1218 if (handlesPageScaleFactor()) { 1219 CGFloat magnification = pluginView()->pageScaleFactor() - [m_pdfLayerController contentScaleFactor]; 1220 1221 // FIXME: Instead of m_lastMousePositionInPluginCoordinates, we should use the zoom origin from PluginView::setPageScaleFactor. 1222 if (magnification) 1223 [m_pdfLayerController magnifyWithMagnification:magnification atPoint:convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates) immediately:NO]; 1224 } else { 1225 // If we don't handle page scale ourselves, we need to respect our parent page's 1226 // scale, which may have changed. 1227 updatePageAndDeviceScaleFactors(); 1228 } 1229 1230 calculateSizes(); 1231 updateScrollbars(); 1232 1233 if (m_activeAnnotation) 1234 m_activeAnnotation->updateGeometry(); 1235 1236 [m_contentLayer setSublayerTransform:transform]; 1237 [CATransaction commit]; 1238} 1239 1240void PDFPlugin::frameDidFinishLoading(uint64_t) 1241{ 1242 ASSERT_NOT_REACHED(); 1243} 1244 1245void PDFPlugin::frameDidFail(uint64_t, bool) 1246{ 1247 ASSERT_NOT_REACHED(); 1248} 1249 1250void PDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&) 1251{ 1252 ASSERT_NOT_REACHED(); 1253} 1254 1255 1256static NSUInteger modifierFlagsFromWebEvent(const WebEvent& event) 1257{ 1258 return (event.shiftKey() ? NSShiftKeyMask : 0) 1259 | (event.controlKey() ? NSControlKeyMask : 0) 1260 | (event.altKey() ? NSAlternateKeyMask : 0) 1261 | (event.metaKey() ? NSCommandKeyMask : 0); 1262} 1263 1264static bool getEventTypeFromWebEvent(const WebEvent& event, NSEventType& eventType) 1265{ 1266 switch (event.type()) { 1267 case WebEvent::KeyDown: 1268 eventType = NSKeyDown; 1269 return true; 1270 case WebEvent::KeyUp: 1271 eventType = NSKeyUp; 1272 return true; 1273 case WebEvent::MouseDown: 1274 switch (static_cast<const WebMouseEvent&>(event).button()) { 1275 case WebMouseEvent::LeftButton: 1276 eventType = NSLeftMouseDown; 1277 return true; 1278 case WebMouseEvent::RightButton: 1279 eventType = NSRightMouseDown; 1280 return true; 1281 default: 1282 return false; 1283 } 1284 case WebEvent::MouseUp: 1285 switch (static_cast<const WebMouseEvent&>(event).button()) { 1286 case WebMouseEvent::LeftButton: 1287 eventType = NSLeftMouseUp; 1288 return true; 1289 case WebMouseEvent::RightButton: 1290 eventType = NSRightMouseUp; 1291 return true; 1292 default: 1293 return false; 1294 } 1295 case WebEvent::MouseMove: 1296 switch (static_cast<const WebMouseEvent&>(event).button()) { 1297 case WebMouseEvent::LeftButton: 1298 eventType = NSLeftMouseDragged; 1299 return true; 1300 case WebMouseEvent::RightButton: 1301 eventType = NSRightMouseDragged; 1302 return true; 1303 case WebMouseEvent::NoButton: 1304 eventType = NSMouseMoved; 1305 return true; 1306 default: 1307 return false; 1308 } 1309 default: 1310 return false; 1311 } 1312} 1313 1314NSEvent *PDFPlugin::nsEventForWebMouseEvent(const WebMouseEvent& event) 1315{ 1316 m_lastMousePositionInPluginCoordinates = convertFromRootViewToPlugin(event.position()); 1317 1318 IntPoint positionInPDFViewCoordinates(convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates)); 1319 1320 NSEventType eventType; 1321 1322 if (!getEventTypeFromWebEvent(event, eventType)) 1323 return 0; 1324 1325 NSUInteger modifierFlags = modifierFlagsFromWebEvent(event); 1326 1327 return [NSEvent mouseEventWithType:eventType location:positionInPDFViewCoordinates modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0]; 1328} 1329 1330void PDFPlugin::updateCursor(const WebMouseEvent& event, UpdateCursorMode mode) 1331{ 1332 HitTestResult hitTestResult = None; 1333 1334 PDFSelection *selectionUnderMouse = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(event.position())]; 1335 if (selectionUnderMouse && [[selectionUnderMouse string] length]) 1336 hitTestResult = Text; 1337 1338 if (hitTestResult == m_lastHitTestResult && mode == UpdateIfNeeded) 1339 return; 1340 1341 webFrame()->page()->send(Messages::WebPageProxy::SetCursor(hitTestResult == Text ? iBeamCursor() : pointerCursor())); 1342 m_lastHitTestResult = hitTestResult; 1343} 1344 1345bool PDFPlugin::handleMouseEvent(const WebMouseEvent& event) 1346{ 1347 PlatformMouseEvent platformEvent = platform(event); 1348 IntPoint mousePosition = convertFromRootViewToPlugin(event.position()); 1349 1350 m_lastMouseEvent = event; 1351 1352 RefPtr<Scrollbar> targetScrollbar; 1353 RefPtr<Scrollbar> targetScrollbarForLastMousePosition; 1354 1355 if (m_verticalScrollbarLayer) { 1356 IntRect verticalScrollbarFrame(m_verticalScrollbarLayer.get().frame); 1357 if (verticalScrollbarFrame.contains(mousePosition)) 1358 targetScrollbar = verticalScrollbar(); 1359 if (verticalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates)) 1360 targetScrollbarForLastMousePosition = verticalScrollbar(); 1361 } 1362 1363 if (m_horizontalScrollbarLayer) { 1364 IntRect horizontalScrollbarFrame(m_horizontalScrollbarLayer.get().frame); 1365 if (horizontalScrollbarFrame.contains(mousePosition)) 1366 targetScrollbar = horizontalScrollbar(); 1367 if (horizontalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates)) 1368 targetScrollbarForLastMousePosition = horizontalScrollbar(); 1369 } 1370 1371 if (m_scrollCornerLayer && IntRect(m_scrollCornerLayer.get().frame).contains(mousePosition)) 1372 return false; 1373 1374 if ([pdfDocument() isLocked]) 1375 return false; 1376 1377 // Right-clicks and Control-clicks always call handleContextMenuEvent as well. 1378 if (event.button() == WebMouseEvent::RightButton || (event.button() == WebMouseEvent::LeftButton && event.controlKey())) 1379 return true; 1380 1381 NSEvent *nsEvent = nsEventForWebMouseEvent(event); 1382 1383 switch (event.type()) { 1384 case WebEvent::MouseMove: 1385 mouseMovedInContentArea(); 1386 updateCursor(event); 1387 1388 if (targetScrollbar) { 1389 if (!targetScrollbarForLastMousePosition) { 1390 targetScrollbar->mouseEntered(); 1391 return true; 1392 } 1393 return targetScrollbar->mouseMoved(platformEvent); 1394 } 1395 1396 if (!targetScrollbar && targetScrollbarForLastMousePosition) 1397 targetScrollbarForLastMousePosition->mouseExited(); 1398 1399 switch (event.button()) { 1400 case WebMouseEvent::LeftButton: 1401 [m_pdfLayerController mouseDragged:nsEvent]; 1402 return true; 1403 case WebMouseEvent::RightButton: 1404 case WebMouseEvent::MiddleButton: 1405 return false; 1406 case WebMouseEvent::NoButton: 1407 [m_pdfLayerController mouseMoved:nsEvent]; 1408 return true; 1409 } 1410 case WebEvent::MouseDown: 1411 switch (event.button()) { 1412 case WebMouseEvent::LeftButton: 1413 if (targetScrollbar) 1414 return targetScrollbar->mouseDown(platformEvent); 1415 1416 [m_pdfLayerController mouseDown:nsEvent]; 1417 return true; 1418 case WebMouseEvent::RightButton: 1419 [m_pdfLayerController rightMouseDown:nsEvent]; 1420 return true; 1421 case WebMouseEvent::MiddleButton: 1422 case WebMouseEvent::NoButton: 1423 return false; 1424 } 1425 case WebEvent::MouseUp: 1426 switch (event.button()) { 1427 case WebMouseEvent::LeftButton: 1428 if (targetScrollbar) 1429 return targetScrollbar->mouseUp(platformEvent); 1430 1431 [m_pdfLayerController mouseUp:nsEvent]; 1432 return true; 1433 case WebMouseEvent::RightButton: 1434 case WebMouseEvent::MiddleButton: 1435 case WebMouseEvent::NoButton: 1436 return false; 1437 } 1438 default: 1439 break; 1440 } 1441 1442 return false; 1443} 1444 1445bool PDFPlugin::handleMouseEnterEvent(const WebMouseEvent& event) 1446{ 1447 mouseEnteredContentArea(); 1448 updateCursor(event, ForceUpdate); 1449 return false; 1450} 1451 1452bool PDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&) 1453{ 1454 mouseExitedContentArea(); 1455 return false; 1456} 1457 1458bool PDFPlugin::showContextMenuAtPoint(const IntPoint& point) 1459{ 1460 FrameView* frameView = webFrame()->coreFrame()->view(); 1461 IntPoint contentsPoint = frameView->contentsToRootView(point); 1462 WebMouseEvent event(WebEvent::MouseDown, WebMouseEvent::RightButton, contentsPoint, contentsPoint, 0, 0, 0, 1, static_cast<WebEvent::Modifiers>(0), monotonicallyIncreasingTime()); 1463 return handleContextMenuEvent(event); 1464} 1465 1466bool PDFPlugin::handleContextMenuEvent(const WebMouseEvent& event) 1467{ 1468 FrameView* frameView = webFrame()->coreFrame()->view(); 1469 IntPoint point = frameView->contentsToScreen(IntRect(frameView->windowToContents(event.position()), IntSize())).location(); 1470 1471 if (NSMenu *nsMenu = [m_pdfLayerController menuForEvent:nsEventForWebMouseEvent(event)]) { 1472 WKPopupContextMenu(nsMenu, point); 1473 return true; 1474 } 1475 1476 return false; 1477} 1478 1479bool PDFPlugin::handleKeyboardEvent(const WebKeyboardEvent& event) 1480{ 1481 NSEventType eventType; 1482 1483 if (!getEventTypeFromWebEvent(event, eventType)) 1484 return false; 1485 1486 NSUInteger modifierFlags = modifierFlagsFromWebEvent(event); 1487 1488 NSEvent *fakeEvent = [NSEvent keyEventWithType:eventType location:NSZeroPoint modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:0 characters:event.text() charactersIgnoringModifiers:event.unmodifiedText() isARepeat:event.isAutoRepeat() keyCode:event.nativeVirtualKeyCode()]; 1489 1490 switch (event.type()) { 1491 case WebEvent::KeyDown: 1492 return [m_pdfLayerController keyDown:fakeEvent]; 1493 default: 1494 return false; 1495 } 1496 1497 return false; 1498} 1499 1500bool PDFPlugin::handleEditingCommand(const String& commandName, const String& argument) 1501{ 1502 if (commandName == "copy") 1503 [m_pdfLayerController copySelection]; 1504 else if (commandName == "selectAll") 1505 [m_pdfLayerController selectAll]; 1506 else if (commandName == "takeFindStringFromSelection") { 1507 NSString *string = [m_pdfLayerController currentSelection].string; 1508 if (string.length) 1509 writeItemsToPasteboard(NSFindPboard, @[ [string dataUsingEncoding:NSUTF8StringEncoding] ], @[ NSPasteboardTypeString ]); 1510 } 1511 1512 return true; 1513} 1514 1515bool PDFPlugin::isEditingCommandEnabled(const String& commandName) 1516{ 1517 if (commandName == "copy" || commandName == "takeFindStringFromSelection") 1518 return [m_pdfLayerController currentSelection]; 1519 1520 if (commandName == "selectAll") 1521 return true; 1522 1523 return false; 1524} 1525 1526void PDFPlugin::setScrollOffset(const IntPoint& offset) 1527{ 1528 m_scrollOffset = IntSize(offset.x(), offset.y()); 1529 1530 [CATransaction begin]; 1531 [m_pdfLayerController setScrollPosition:offset]; 1532 1533 if (m_activeAnnotation) 1534 m_activeAnnotation->updateGeometry(); 1535 1536 [CATransaction commit]; 1537} 1538 1539void PDFPlugin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) 1540{ 1541 if (scrollbar == horizontalScrollbar()) 1542 [m_horizontalScrollbarLayer setNeedsDisplay]; 1543 else if (scrollbar == verticalScrollbar()) 1544 [m_verticalScrollbarLayer setNeedsDisplay]; 1545} 1546 1547void PDFPlugin::invalidateScrollCornerRect(const IntRect& rect) 1548{ 1549 [m_scrollCornerLayer setNeedsDisplay]; 1550} 1551 1552bool PDFPlugin::isFullFramePlugin() 1553{ 1554 // <object> or <embed> plugins will appear to be in their parent frame, so we have to 1555 // check whether our frame's widget is exactly our PluginView. 1556 Document* document = webFrame()->coreFrame()->document(); 1557 return document->isPluginDocument() && static_cast<PluginDocument*>(document)->pluginWidget() == pluginView(); 1558} 1559 1560bool PDFPlugin::handlesPageScaleFactor() 1561{ 1562 return webFrame()->isMainFrame() && isFullFramePlugin(); 1563} 1564 1565void PDFPlugin::clickedLink(NSURL *url) 1566{ 1567 Frame* frame = webFrame()->coreFrame(); 1568 1569 RefPtr<Event> coreEvent; 1570 if (m_lastMouseEvent.type() != WebEvent::NoType) 1571 coreEvent = MouseEvent::create(eventNames().clickEvent, frame->document()->defaultView(), platform(m_lastMouseEvent), 0, 0); 1572 1573 frame->loader().urlSelected(url, emptyString(), coreEvent.get(), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer); 1574} 1575 1576void PDFPlugin::setActiveAnnotation(PDFAnnotation *annotation) 1577{ 1578 if (!supportsForms()) 1579 return; 1580 1581 if (m_activeAnnotation) 1582 m_activeAnnotation->commit(); 1583 1584 if (annotation) { 1585 if ([annotation isKindOfClass:pdfAnnotationTextWidgetClass()] && static_cast<PDFAnnotationTextWidget *>(annotation).isReadOnly) { 1586 m_activeAnnotation = 0; 1587 return; 1588 } 1589 1590 m_activeAnnotation = PDFPluginAnnotation::create(annotation, m_pdfLayerController.get(), this); 1591 m_activeAnnotation->attach(m_annotationContainer.get()); 1592 } else 1593 m_activeAnnotation = 0; 1594} 1595 1596bool PDFPlugin::supportsForms() 1597{ 1598 // FIXME: We support forms for full-main-frame and <iframe> PDFs, but not <embed> or <object>, because those cases do not have their own Document into which to inject form elements. 1599 return isFullFramePlugin(); 1600} 1601 1602void PDFPlugin::notifyContentScaleFactorChanged(CGFloat scaleFactor) 1603{ 1604 if (handlesPageScaleFactor()) 1605 pluginView()->setPageScaleFactor(scaleFactor, IntPoint()); 1606 1607 calculateSizes(); 1608 updateScrollbars(); 1609} 1610 1611void PDFPlugin::notifyDisplayModeChanged(int) 1612{ 1613 calculateSizes(); 1614 updateScrollbars(); 1615} 1616 1617PassRefPtr<SharedBuffer> PDFPlugin::liveResourceData() const 1618{ 1619 NSData *pdfData = liveData(); 1620 1621 if (!pdfData) 1622 return 0; 1623 1624 return SharedBuffer::wrapNSData(pdfData); 1625} 1626 1627void PDFPlugin::saveToPDF() 1628{ 1629 // FIXME: We should probably notify the user that they can't save before the document is finished loading. 1630 // PDFViewController does an NSBeep(), but that seems insufficient. 1631 if (!pdfDocument()) 1632 return; 1633 1634 NSData *data = liveData(); 1635 webFrame()->page()->savePDFToFileInDownloadsFolder(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length]); 1636} 1637 1638void PDFPlugin::openWithNativeApplication() 1639{ 1640 if (!m_temporaryPDFUUID) { 1641 // FIXME: We should probably notify the user that they can't save before the document is finished loading. 1642 // PDFViewController does an NSBeep(), but that seems insufficient. 1643 if (!pdfDocument()) 1644 return; 1645 1646 NSData *data = liveData(); 1647 1648 m_temporaryPDFUUID = WebCore::createCanonicalUUIDString(); 1649 ASSERT(m_temporaryPDFUUID); 1650 1651 webFrame()->page()->savePDFToTemporaryFolderAndOpenWithNativeApplication(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length], m_temporaryPDFUUID); 1652 return; 1653 } 1654 1655 webFrame()->page()->send(Messages::WebPageProxy::OpenPDFFromTemporaryFolderWithNativeApplication(m_temporaryPDFUUID)); 1656} 1657 1658void PDFPlugin::writeItemsToPasteboard(NSString *pasteboardName, NSArray *items, NSArray *types) 1659{ 1660 Vector<String> pasteboardTypes; 1661 1662 for (NSString *type in types) 1663 pasteboardTypes.append(type); 1664 1665 uint64_t newChangeCount; 1666 WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebContext::SetPasteboardTypes(pasteboardName, pasteboardTypes), 1667 Messages::WebContext::SetPasteboardTypes::Reply(newChangeCount), 0); 1668 1669 for (NSUInteger i = 0, count = items.count; i < count; ++i) { 1670 NSString *type = [types objectAtIndex:i]; 1671 NSData *data = [items objectAtIndex:i]; 1672 1673 // We don't expect the data for any items to be empty, but aren't completely sure. 1674 // Avoid crashing in the SharedMemory constructor in release builds if we're wrong. 1675 ASSERT(data.length); 1676 if (!data.length) 1677 continue; 1678 1679 if ([type isEqualToString:NSStringPboardType] || [type isEqualToString:NSPasteboardTypeString]) { 1680 RetainPtr<NSString> plainTextString = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); 1681 WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebContext::SetPasteboardStringForType(pasteboardName, type, plainTextString.get()), 1682 Messages::WebContext::SetPasteboardStringForType::Reply(newChangeCount), 0); 1683 } else { 1684 RefPtr<SharedBuffer> buffer = SharedBuffer::wrapNSData(data); 1685 1686 if (!buffer) 1687 continue; 1688 1689 SharedMemory::Handle handle; 1690 RefPtr<SharedMemory> sharedMemory = SharedMemory::create(buffer->size()); 1691 memcpy(sharedMemory->data(), buffer->data(), buffer->size()); 1692 sharedMemory->createHandle(handle, SharedMemory::ReadOnly); 1693 WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebContext::SetPasteboardBufferForType(pasteboardName, type, handle, buffer->size()), 1694 Messages::WebContext::SetPasteboardBufferForType::Reply(newChangeCount), 0); 1695 } 1696 } 1697} 1698 1699void PDFPlugin::showDefinitionForAttributedString(NSAttributedString *string, CGPoint point) 1700{ 1701 DictionaryPopupInfo dictionaryPopupInfo; 1702 dictionaryPopupInfo.origin = convertFromPDFViewToRootView(IntPoint(point)); 1703 1704 AttributedString attributedString; 1705 attributedString.string = string; 1706 1707 webFrame()->page()->send(Messages::WebPageProxy::DidPerformDictionaryLookup(attributedString, dictionaryPopupInfo)); 1708} 1709 1710unsigned PDFPlugin::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount) 1711{ 1712 if (!target.length()) 1713 return 0; 1714 1715 int nsOptions = (options & FindOptionsCaseInsensitive) ? NSCaseInsensitiveSearch : 0; 1716 1717 return [[pdfDocument() findString:target withOptions:nsOptions] count]; 1718} 1719 1720PDFSelection *PDFPlugin::nextMatchForString(const String& target, BOOL searchForward, BOOL caseSensitive, BOOL wrapSearch, PDFSelection *initialSelection, BOOL startInSelection) 1721{ 1722 if (!target.length()) 1723 return nil; 1724 1725 NSStringCompareOptions options = 0; 1726 if (!searchForward) 1727 options |= NSBackwardsSearch; 1728 1729 if (!caseSensitive) 1730 options |= NSCaseInsensitiveSearch; 1731 1732 PDFDocument *document = pdfDocument().get(); 1733 1734 PDFSelection *selectionForInitialSearch = [initialSelection copy]; 1735 if (startInSelection) { 1736 // Initially we want to include the selected text in the search. So we must modify the starting search 1737 // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before 1738 // the current selection (if searching forwards) or after (if searching backwards). 1739 int initialSelectionLength = [[initialSelection string] length]; 1740 if (searchForward) { 1741 [selectionForInitialSearch extendSelectionAtStart:1]; 1742 [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength]; 1743 } else { 1744 [selectionForInitialSearch extendSelectionAtEnd:1]; 1745 [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength]; 1746 } 1747 } 1748 1749 PDFSelection *foundSelection = [document findString:target fromSelection:selectionForInitialSearch withOptions:options]; 1750 [selectionForInitialSearch release]; 1751 1752 // If we first searched in the selection, and we found the selection, search again from just past the selection. 1753 if (startInSelection && [foundSelection isEqual:initialSelection]) 1754 foundSelection = [document findString:target fromSelection:initialSelection withOptions:options]; 1755 1756 if (!foundSelection && wrapSearch) 1757 foundSelection = [document findString:target fromSelection:nil withOptions:options]; 1758 1759 return foundSelection; 1760} 1761 1762bool PDFPlugin::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount) 1763{ 1764 BOOL searchForward = !(options & FindOptionsBackwards); 1765 BOOL caseSensitive = !(options & FindOptionsCaseInsensitive); 1766 BOOL wrapSearch = options & FindOptionsWrapAround; 1767 1768 unsigned matchCount; 1769 if (!maxMatchCount) { 1770 // If the max was zero, any result means we exceeded the max. We can skip computing the actual count. 1771 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount); 1772 } else { 1773 matchCount = countFindMatches(target, options, maxMatchCount); 1774 if (matchCount > maxMatchCount) 1775 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount); 1776 } 1777 1778 if (target.isEmpty()) { 1779 PDFSelection* searchSelection = [m_pdfLayerController searchSelection]; 1780 [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES]; 1781 [m_pdfLayerController setSearchSelection:searchSelection]; 1782 m_lastFoundString = emptyString(); 1783 return false; 1784 } 1785 1786 if (m_lastFoundString == target) { 1787 PDFSelection *selection = nextMatchForString(target, searchForward, caseSensitive, wrapSearch, [m_pdfLayerController searchSelection], NO); 1788 if (!selection) 1789 return false; 1790 1791 [m_pdfLayerController setSearchSelection:selection]; 1792 [m_pdfLayerController gotoSelection:selection]; 1793 } else { 1794 [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES]; 1795 m_lastFoundString = target; 1796 } 1797 1798 return matchCount > 0; 1799} 1800 1801bool PDFPlugin::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point) 1802{ 1803 IntPoint localPoint = convertFromRootViewToPlugin(roundedIntPoint(point)); 1804 PDFSelection* lookupSelection = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(localPoint)]; 1805 1806 if ([[lookupSelection string] length]) 1807 [m_pdfLayerController searchInDictionaryWithSelection:lookupSelection]; 1808 1809 return true; 1810} 1811 1812void PDFPlugin::focusNextAnnotation() 1813{ 1814 [m_pdfLayerController activateNextAnnotation:false]; 1815} 1816 1817void PDFPlugin::focusPreviousAnnotation() 1818{ 1819 [m_pdfLayerController activateNextAnnotation:true]; 1820} 1821 1822void PDFPlugin::notifySelectionChanged(PDFSelection *) 1823{ 1824 webFrame()->page()->didChangeSelection(); 1825} 1826 1827String PDFPlugin::getSelectionString() const 1828{ 1829 return [[m_pdfLayerController currentSelection] string]; 1830} 1831 1832void PDFPlugin::performWebSearch(NSString *string) 1833{ 1834 webFrame()->page()->send(Messages::WebPageProxy::SearchTheWeb(string)); 1835} 1836 1837void PDFPlugin::performSpotlightSearch(NSString *string) 1838{ 1839 webFrame()->page()->send(Messages::WebPageProxy::SearchWithSpotlight(string)); 1840} 1841 1842bool PDFPlugin::handleWheelEvent(const WebWheelEvent& event) 1843{ 1844 PDFDisplayMode displayMode = [m_pdfLayerController displayMode]; 1845 1846 if (displayMode == kPDFDisplaySinglePageContinuous || displayMode == kPDFDisplayTwoUpContinuous) 1847 return ScrollableArea::handleWheelEvent(platform(event)); 1848 1849 NSUInteger currentPageIndex = [m_pdfLayerController currentPageIndex]; 1850 bool inFirstPage = !currentPageIndex; 1851 bool inLastPage = [m_pdfLayerController lastPageIndex] == currentPageIndex; 1852 1853 bool atScrollTop = !scrollPosition().y(); 1854 bool atScrollBottom = scrollPosition().y() == maximumScrollPosition().y(); 1855 1856 bool inMomentumScroll = event.momentumPhase() != WebWheelEvent::PhaseNone; 1857 1858 int scrollMagnitudeThresholdForPageFlip = defaultScrollMagnitudeThresholdForPageFlip; 1859 1860 // Imprecise input devices should have a lower threshold so that "clicky" scroll wheels can flip pages. 1861 if (!event.hasPreciseScrollingDeltas()) 1862 scrollMagnitudeThresholdForPageFlip = 0; 1863 1864 if (atScrollBottom && !inLastPage && event.delta().height() < 0) { 1865 if (event.delta().height() <= -scrollMagnitudeThresholdForPageFlip && !inMomentumScroll) 1866 [m_pdfLayerController gotoNextPage]; 1867 return true; 1868 } 1869 1870 if (atScrollTop && !inFirstPage && event.delta().height() > 0) { 1871 if (event.delta().height() >= scrollMagnitudeThresholdForPageFlip && !inMomentumScroll) { 1872 [CATransaction begin]; 1873 [m_pdfLayerController gotoPreviousPage]; 1874 scrollToOffsetWithoutAnimation(maximumScrollPosition()); 1875 [CATransaction commit]; 1876 } 1877 return true; 1878 } 1879 1880 return ScrollableArea::handleWheelEvent(platform(event)); 1881} 1882 1883NSData *PDFPlugin::liveData() const 1884{ 1885 if (m_activeAnnotation) 1886 m_activeAnnotation->commit(); 1887 1888 // Save data straight from the resource instead of PDFKit if the document is 1889 // untouched by the user, so that PDFs which PDFKit can't display will still be downloadable. 1890 if (m_pdfDocumentWasMutated) 1891 return [m_pdfDocument dataRepresentation]; 1892 1893 return rawData(); 1894} 1895 1896NSObject *PDFPlugin::accessibilityObject() const 1897{ 1898 return m_accessibilityObject.get(); 1899} 1900 1901} // namespace WebKit 1902 1903#endif // ENABLE(PDFKIT_PLUGIN) 1904