2 * Copyright (C) 2009, 2011, 2012, 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #if ENABLE(PDFKIT_PLUGIN)
31 #import "ArgumentCoders.h"
32 #import "DataReference.h"
33 #import "PDFAnnotationTextWidgetDetails.h"
34 #import "PDFLayerControllerSPI.h"
35 #import "PDFPluginAnnotation.h"
36 #import "PDFPluginPasswordField.h"
37 #import "PluginView.h"
38 #import "WKAccessibilityWebPageObjectMac.h"
39 #import "WKPageFindMatchesClient.h"
40 #import "WebCoreArgumentCoders.h"
42 #import "WebEventConversion.h"
44 #import "WebPageProxyMessages.h"
45 #import "WebPasteboardProxyMessages.h"
46 #import "WebProcess.h"
47 #import <JavaScriptCore/JSContextRef.h>
48 #import <JavaScriptCore/JSObjectRef.h>
49 #import <JavaScriptCore/JSStringRef.h>
50 #import <JavaScriptCore/JSStringRefCF.h>
51 #import <PDFKit/PDFKit.h>
52 #import <QuartzCore/QuartzCore.h>
53 #import <WebCore/AXObjectCache.h>
54 #import <WebCore/ArchiveResource.h>
55 #import <WebCore/Chrome.h>
56 #import <WebCore/Cursor.h>
57 #import <WebCore/DictionaryLookup.h>
58 #import <WebCore/DocumentLoader.h>
59 #import <WebCore/EventNames.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/HTMLPlugInElement.h>
69 #import <WebCore/LocalizedStrings.h>
70 #import <WebCore/MainFrame.h>
71 #import <WebCore/MouseEvent.h>
72 #import <WebCore/Page.h>
73 #import <WebCore/Pasteboard.h>
74 #import <WebCore/PluginData.h>
75 #import <WebCore/PluginDocument.h>
76 #import <WebCore/RenderBoxModelObject.h>
77 #import <WebCore/ScrollAnimator.h>
78 #import <WebCore/ScrollbarTheme.h>
79 #import <WebCore/Settings.h>
80 #import <WebCore/UUID.h>
81 #import <WebCore/WheelEventTestTrigger.h>
82 #import <WebKitSystemInterface.h>
83 #import <wtf/CurrentTime.h>
85 using namespace WebCore;
87 // Set overflow: hidden on the annotation container so <input> elements scrolled out of view don't show
88 // scrollbars on the body. We can't add annotations directly to the body, because overflow: hidden on the body
89 // will break rubber-banding.
90 static const char* annotationStyle =
91 "#annotationContainer {"
93 " position: absolute; "
94 " pointer-events: none; "
99 " display: -webkit-box; "
100 " -webkit-box-align: center; "
101 " -webkit-box-pack: center; "
104 " position: absolute; "
105 " pointer-events: auto; "
107 "textarea.annotation { "
110 "input.annotation[type='password'] { "
111 " position: static; "
113 " margin-top: 100px; "
116 // In non-continuous modes, a single scroll event with a magnitude of >= 20px
117 // will jump to the next or previous page, to match PDFKit behavior.
118 static const int defaultScrollMagnitudeThresholdForPageFlip = 20;
120 @interface WKPDFPluginAccessibilityObject : NSObject {
121 PDFLayerController *_pdfLayerController;
123 WebKit::PDFPlugin* _pdfPlugin;
126 @property (assign) PDFLayerController *pdfLayerController;
127 @property (assign) NSObject *parent;
128 @property (assign) WebKit::PDFPlugin* pdfPlugin;
130 - (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin;
134 @implementation WKPDFPluginAccessibilityObject
136 @synthesize parent=_parent;
137 @synthesize pdfPlugin=_pdfPlugin;
139 - (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
141 if (!(self = [super init]))
149 - (PDFLayerController *)pdfLayerController
151 return _pdfLayerController;
154 - (void)setPdfLayerController:(PDFLayerController *)pdfLayerController
156 _pdfLayerController = pdfLayerController;
157 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
158 [_pdfLayerController setAccessibilityParent:self];
162 - (BOOL)accessibilityIsIgnored
167 // This is called by PDFAccessibilityNodes from inside PDFKit to get final bounds.
168 - (NSRect)convertRectToScreenSpace:(NSRect)rect
170 return _pdfPlugin->convertFromPDFViewToScreen(rect);
173 - (id)accessibilityAttributeValue:(NSString *)attribute
175 if ([attribute isEqualToString:NSAccessibilityParentAttribute])
177 if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
178 return [_parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
179 if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
180 return [_parent accessibilityAttributeValue:NSAccessibilityWindowAttribute];
181 if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
182 return [NSValue valueWithSize:_pdfPlugin->boundsOnScreen().size()];
183 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
184 return [_parent accessibilityAttributeValue:NSAccessibilityEnabledAttribute];
185 if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
186 return [NSValue valueWithPoint:_pdfPlugin->boundsOnScreen().location()];
188 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
189 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
190 return @[ _pdfLayerController ];
191 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
192 return NSAccessibilityGroupRole;
194 if ([attribute isEqualToString:NSAccessibilityValueAttribute])
195 return [_pdfLayerController accessibilityValueAttribute];
196 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
197 return [_pdfLayerController accessibilitySelectedTextAttribute];
198 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
199 return [_pdfLayerController accessibilitySelectedTextRangeAttribute];
200 if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
201 return [_pdfLayerController accessibilityNumberOfCharactersAttribute];
202 if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
203 return [_pdfLayerController accessibilityVisibleCharacterRangeAttribute];
204 if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
205 return [_pdfLayerController accessibilityRoleAttribute];
206 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
207 return [_pdfLayerController accessibilityRoleDescriptionAttribute];
208 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
209 return [_parent accessibilityAttributeValue:NSAccessibilityFocusedAttribute];
215 - (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter
217 if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
218 NSRect boundsInPDFViewCoordinates = [[_pdfLayerController accessibilityBoundsForRangeAttributeForParameter:parameter] rectValue];
219 NSRect boundsInScreenCoordinates = _pdfPlugin->convertFromPDFViewToScreen(boundsInPDFViewCoordinates);
220 return [NSValue valueWithRect:boundsInScreenCoordinates];
223 if ([attribute isEqualToString:NSAccessibilityLineForIndexParameterizedAttribute])
224 return [_pdfLayerController accessibilityLineForIndexAttributeForParameter:parameter];
225 if ([attribute isEqualToString:NSAccessibilityRangeForLineParameterizedAttribute])
226 return [_pdfLayerController accessibilityRangeForLineAttributeForParameter:parameter];
227 if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute])
228 return [_pdfLayerController accessibilityStringForRangeAttributeForParameter:parameter];
233 - (CPReadingModel *)readingModel
235 return [_pdfLayerController readingModel];
238 - (NSArray *)accessibilityAttributeNames
240 static NSArray *attributeNames = 0;
242 if (!attributeNames) {
244 NSAccessibilityParentAttribute,
245 NSAccessibilityWindowAttribute,
246 NSAccessibilityTopLevelUIElementAttribute,
247 NSAccessibilityRoleDescriptionAttribute,
248 NSAccessibilitySizeAttribute,
249 NSAccessibilityEnabledAttribute,
250 NSAccessibilityPositionAttribute,
251 NSAccessibilityFocusedAttribute,
252 // PDFLayerController has its own accessibilityChildren.
253 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
254 NSAccessibilityChildrenAttribute
256 NSAccessibilityRoleAttribute,
257 NSAccessibilityValueAttribute,
258 NSAccessibilitySelectedTextAttribute,
259 NSAccessibilitySelectedTextRangeAttribute,
260 NSAccessibilityNumberOfCharactersAttribute,
261 NSAccessibilityVisibleCharacterRangeAttribute
264 [attributeNames retain];
267 return attributeNames;
270 - (NSArray *)accessibilityActionNames
272 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
275 static NSArray *actionNames = 0;
278 actionNames = [[NSArray arrayWithObject:NSAccessibilityShowMenuAction] retain];
284 - (void)accessibilityPerformAction:(NSString *)action
286 if ([action isEqualToString:NSAccessibilityShowMenuAction])
287 _pdfPlugin->showContextMenuAtPoint(IntRect(IntPoint(), _pdfPlugin->size()).center());
290 - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute
292 return [_pdfLayerController accessibilityIsAttributeSettable:attribute];
295 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
297 return [_pdfLayerController accessibilitySetValue:value forAttribute:attribute];
300 - (NSArray *)accessibilityParameterizedAttributeNames
302 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
305 return [_pdfLayerController accessibilityParameterizedAttributeNames];
309 - (id)accessibilityFocusedUIElement
311 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
312 if (WebKit::PDFPluginAnnotation* activeAnnotation = _pdfPlugin->activeAnnotation()) {
313 if (AXObjectCache* existingCache = _pdfPlugin->axObjectCache()) {
314 if (AccessibilityObject* object = existingCache->getOrCreate(activeAnnotation->element()))
315 #pragma clang diagnostic push
316 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
317 return [object->wrapper() accessibilityAttributeValue:@"_AXAssociatedPluginParent"];
318 #pragma clang diagnostic pop
327 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
328 - (id)accessibilityAssociatedControlForAnnotation:(PDFAnnotation *)annotation
330 // Only active annotations seem to have their associated controls available.
331 WebKit::PDFPluginAnnotation* activeAnnotation = _pdfPlugin->activeAnnotation();
332 if (!activeAnnotation || ![activeAnnotation->annotation() isEqual:annotation])
335 AXObjectCache* cache = _pdfPlugin->axObjectCache();
339 AccessibilityObject* object = cache->getOrCreate(activeAnnotation->element());
343 return object->wrapper();
347 - (id)accessibilityHitTest:(NSPoint)point
349 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
350 point = _pdfPlugin->convertFromRootViewToPDFView(IntPoint(point));
351 return [_pdfLayerController accessibilityHitTest:point];
360 @interface WKPDFPluginScrollbarLayer : CALayer {
361 WebKit::PDFPlugin* _pdfPlugin;
364 @property (assign) WebKit::PDFPlugin* pdfPlugin;
368 @implementation WKPDFPluginScrollbarLayer
370 @synthesize pdfPlugin=_pdfPlugin;
372 - (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
374 if (!(self = [super init]))
382 - (id<CAAction>)actionForKey:(NSString *)key
387 - (void)drawInContext:(CGContextRef)ctx
389 _pdfPlugin->paintControlForLayerInContext(self, ctx);
394 @interface WKPDFLayerControllerDelegate : NSObject<PDFLayerControllerDelegate> {
395 WebKit::PDFPlugin* _pdfPlugin;
398 @property (assign) WebKit::PDFPlugin* pdfPlugin;
402 @implementation WKPDFLayerControllerDelegate
404 @synthesize pdfPlugin=_pdfPlugin;
406 - (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
408 if (!(self = [super init]))
416 - (void)updateScrollPosition:(CGPoint)newPosition
418 _pdfPlugin->notifyScrollPositionChanged(IntPoint(newPosition));
421 - (void)writeItemsToPasteboard:(NSArray *)items withTypes:(NSArray *)types
423 #pragma clang diagnostic push
424 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
425 _pdfPlugin->writeItemsToPasteboard(NSGeneralPboard, items, types);
426 #pragma clang diagnostic pop
429 - (void)showDefinitionForAttributedString:(NSAttributedString *)string atPoint:(CGPoint)point
431 _pdfPlugin->showDefinitionForAttributedString(string, point);
434 - (void)performWebSearch:(NSString *)string
436 _pdfPlugin->performWebSearch(string);
439 - (void)performSpotlightSearch:(NSString *)string
441 _pdfPlugin->performSpotlightSearch(string);
444 - (void)openWithNativeApplication
446 _pdfPlugin->openWithNativeApplication();
451 _pdfPlugin->saveToPDF();
454 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController clickedLinkWithURL:(NSURL *)url
456 _pdfPlugin->clickedLink(url);
459 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeActiveAnnotation:(PDFAnnotation *)annotation
461 _pdfPlugin->setActiveAnnotation(annotation);
464 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeContentScaleFactor:(CGFloat)scaleFactor
466 _pdfPlugin->notifyContentScaleFactorChanged(scaleFactor);
469 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeDisplayMode:(int)mode
471 _pdfPlugin->notifyDisplayModeChanged(mode);
474 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeSelection:(PDFSelection *)selection
476 _pdfPlugin->notifySelectionChanged(selection);
479 - (void)setMouseCursor:(PDFLayerControllerCursorType)cursorType
481 _pdfPlugin->notifyCursorChanged(cursorType);
486 @interface PDFViewLayout
487 - (NSPoint)convertPoint:(NSPoint)point toPage:(PDFPage *)page forScaleFactor:(CGFloat)scaleFactor;
488 - (NSRect)convertRect:(NSRect)rect fromPage:(PDFPage *) page forScaleFactor:(CGFloat) scaleFactor;
489 - (PDFPage *)pageNearestPoint:(NSPoint)point currentPage:(PDFPage *)currentPage;
492 static const char* postScriptMIMEType = "application/postscript";
493 const uint64_t pdfDocumentRequestID = 1; // PluginController supports loading multiple streams, but we only need one for PDF.
495 static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values)
498 if (CGPDFDictionaryGetArray(subtree, "Names", &names)) {
499 size_t nameCount = CGPDFArrayGetCount(names) / 2;
500 for (size_t i = 0; i < nameCount; ++i) {
501 CGPDFObjectRef object;
502 CGPDFArrayGetObject(names, 2 * i + 1, &object);
503 values.append(object);
509 if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids))
512 size_t kidCount = CGPDFArrayGetCount(kids);
513 for (size_t i = 0; i < kidCount; ++i) {
514 CGPDFDictionaryRef kid;
515 if (!CGPDFArrayGetDictionary(kids, i, &kid))
517 appendValuesInPDFNameSubtreeToVector(kid, values);
521 static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues)
523 appendValuesInPDFNameSubtreeToVector(tree, allValues);
526 static void getAllScriptsInPDFDocument(CGPDFDocumentRef pdfDocument, Vector<RetainPtr<CFStringRef>>& scripts)
531 CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument);
535 // Get the dictionary of all document-level name trees.
536 CGPDFDictionaryRef namesDictionary;
537 if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary))
540 // Get the document-level "JavaScript" name tree.
541 CGPDFDictionaryRef javaScriptNameTree;
542 if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree))
545 // The names are arbitrary. We are only interested in the values.
546 Vector<CGPDFObjectRef> objects;
547 getAllValuesInPDFNameTree(javaScriptNameTree, objects);
548 size_t objectCount = objects.size();
550 for (size_t i = 0; i < objectCount; ++i) {
551 CGPDFDictionaryRef javaScriptAction;
552 if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction))
555 // A JavaScript action must have an action type of "JavaScript".
556 const char* actionType;
557 if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript"))
560 const UInt8* bytes = 0;
562 CGPDFStreamRef stream;
563 CGPDFStringRef string;
564 RetainPtr<CFDataRef> data;
565 if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) {
566 CGPDFDataFormat format;
567 data = adoptCF(CGPDFStreamCopyData(stream, &format));
570 bytes = CFDataGetBytePtr(data.get());
571 length = CFDataGetLength(data.get());
572 } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) {
573 bytes = CGPDFStringGetBytePtr(string);
574 length = CGPDFStringGetLength(string);
579 CFStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? kCFStringEncodingUnicode : kCFStringEncodingUTF8;
580 RetainPtr<CFStringRef> script = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, bytes, length, encoding, true));
584 scripts.append(script);
590 using namespace HTMLNames;
592 Ref<PDFPlugin> PDFPlugin::create(WebFrame* frame)
594 return adoptRef(*new PDFPlugin(frame));
597 PDFPlugin::PDFPlugin(WebFrame* frame)
598 : Plugin(PDFPluginType)
600 , m_isPostScript(false)
601 , m_pdfDocumentWasMutated(false)
602 , m_containerLayer(adoptNS([[CALayer alloc] init]))
603 , m_contentLayer(adoptNS([[CALayer alloc] init]))
604 , m_scrollCornerLayer(adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]))
605 , m_pdfLayerController(adoptNS([[pdfLayerControllerClass() alloc] init]))
606 , m_pdfLayerControllerDelegate(adoptNS([[WKPDFLayerControllerDelegate alloc] initWithPDFPlugin:this]))
608 m_pdfLayerController.get().delegate = m_pdfLayerControllerDelegate.get();
609 m_pdfLayerController.get().parentLayer = m_contentLayer.get();
611 if (supportsForms()) {
612 Document* document = webFrame()->coreFrame()->document();
613 m_annotationContainer = document->createElement(divTag, false);
614 m_annotationContainer->setAttributeWithoutSynchronization(idAttr, AtomicString("annotationContainer", AtomicString::ConstructFromLiteral));
616 auto annotationStyleElement = document->createElement(styleTag, false);
617 annotationStyleElement->setTextContent(annotationStyle);
619 m_annotationContainer->appendChild(annotationStyleElement);
620 document->bodyOrFrameset()->appendChild(*m_annotationContainer);
623 m_accessibilityObject = adoptNS([[WKPDFPluginAccessibilityObject alloc] initWithPDFPlugin:this]);
624 [m_accessibilityObject setPdfLayerController:m_pdfLayerController.get()];
625 [m_accessibilityObject setParent:webFrame()->page()->accessibilityRemoteObject()];
627 [m_containerLayer addSublayer:m_contentLayer.get()];
628 [m_containerLayer addSublayer:m_scrollCornerLayer.get()];
631 PDFPlugin::~PDFPlugin()
635 PluginInfo PDFPlugin::pluginInfo()
638 info.name = builtInPDFPluginName();
639 info.isApplicationPlugin = true;
640 info.clientLoadPolicy = PluginLoadClientPolicyUndefined;
642 MimeClassInfo pdfMimeClassInfo;
643 pdfMimeClassInfo.type = "application/pdf";
644 pdfMimeClassInfo.desc = pdfDocumentTypeDescription();
645 pdfMimeClassInfo.extensions.append("pdf");
646 info.mimes.append(pdfMimeClassInfo);
648 MimeClassInfo textPDFMimeClassInfo;
649 textPDFMimeClassInfo.type = "text/pdf";
650 textPDFMimeClassInfo.desc = pdfDocumentTypeDescription();
651 textPDFMimeClassInfo.extensions.append("pdf");
652 info.mimes.append(textPDFMimeClassInfo);
654 MimeClassInfo postScriptMimeClassInfo;
655 postScriptMimeClassInfo.type = postScriptMIMEType;
656 postScriptMimeClassInfo.desc = postScriptDocumentTypeDescription();
657 postScriptMimeClassInfo.extensions.append("ps");
658 info.mimes.append(postScriptMimeClassInfo);
663 void PDFPlugin::updateScrollbars()
665 bool hadScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
667 if (m_horizontalScrollbar) {
668 if (m_size.width() >= m_pdfDocumentSize.width())
669 destroyScrollbar(HorizontalScrollbar);
670 } else if (m_size.width() < m_pdfDocumentSize.width())
671 m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
673 if (m_verticalScrollbar) {
674 if (m_size.height() >= m_pdfDocumentSize.height())
675 destroyScrollbar(VerticalScrollbar);
676 } else if (m_size.height() < m_pdfDocumentSize.height())
677 m_verticalScrollbar = createScrollbar(VerticalScrollbar);
679 IntSize scrollbarSpace = scrollbarIntrusion();
681 int pageStep = m_pageBoxes.isEmpty() ? 0 : m_pageBoxes[0].height();
683 if (m_horizontalScrollbar) {
684 m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
685 m_horizontalScrollbar->setProportion(m_size.width() - scrollbarSpace.width(), m_pdfDocumentSize.width());
686 IntRect scrollbarRect(pluginView()->x(), pluginView()->y() + m_size.height() - m_horizontalScrollbar->height(), m_size.width(), m_horizontalScrollbar->height());
687 if (m_verticalScrollbar)
688 scrollbarRect.contract(m_verticalScrollbar->width(), 0);
689 m_horizontalScrollbar->setFrameRect(scrollbarRect);
691 if (m_verticalScrollbar) {
692 m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
693 m_verticalScrollbar->setProportion(m_size.height() - scrollbarSpace.height(), m_pdfDocumentSize.height());
694 IntRect scrollbarRect(IntRect(pluginView()->x() + m_size.width() - m_verticalScrollbar->width(), pluginView()->y(), m_verticalScrollbar->width(), m_size.height()));
695 if (m_horizontalScrollbar)
696 scrollbarRect.contract(0, m_horizontalScrollbar->height());
697 m_verticalScrollbar->setFrameRect(scrollbarRect);
700 FrameView* frameView = m_frame->coreFrame()->view();
704 bool hasScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
705 if (hadScrollbars != hasScrollbars) {
707 frameView->addScrollableArea(this);
709 frameView->removeScrollableArea(this);
711 frameView->setNeedsLayout();
714 if (m_verticalScrollbarLayer) {
715 m_verticalScrollbarLayer.get().frame = verticalScrollbar()->frameRect();
716 [m_verticalScrollbarLayer setNeedsDisplay];
719 if (m_horizontalScrollbarLayer) {
720 m_horizontalScrollbarLayer.get().frame = horizontalScrollbar()->frameRect();
721 [m_horizontalScrollbarLayer setNeedsDisplay];
724 if (m_scrollCornerLayer) {
725 m_scrollCornerLayer.get().frame = scrollCornerRect();
726 [m_scrollCornerLayer setNeedsDisplay];
730 PluginView* PDFPlugin::pluginView()
732 return static_cast<PluginView*>(controller());
735 const PluginView* PDFPlugin::pluginView() const
737 return static_cast<const PluginView*>(controller());
740 Ref<Scrollbar> PDFPlugin::createScrollbar(ScrollbarOrientation orientation)
742 auto widget = Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar);
743 if (orientation == HorizontalScrollbar) {
744 m_horizontalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
745 [m_containerLayer addSublayer:m_horizontalScrollbarLayer.get()];
747 m_verticalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
748 [m_containerLayer addSublayer:m_verticalScrollbarLayer.get()];
750 didAddScrollbar(widget.ptr(), orientation);
751 if (Frame* frame = webFrame()->coreFrame()) {
752 if (Page* page = frame->page()) {
753 if (page->expectsWheelEventTriggers())
754 scrollAnimator().setWheelEventTestTrigger(page->testTrigger());
757 pluginView()->frame()->view()->addChild(widget);
761 void PDFPlugin::destroyScrollbar(ScrollbarOrientation orientation)
763 RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_horizontalScrollbar : m_verticalScrollbar;
767 willRemoveScrollbar(scrollbar.get(), orientation);
768 scrollbar->removeFromParent();
771 if (orientation == HorizontalScrollbar) {
772 [m_horizontalScrollbarLayer removeFromSuperlayer];
773 m_horizontalScrollbarLayer = 0;
775 [m_verticalScrollbarLayer removeFromSuperlayer];
776 m_verticalScrollbarLayer = 0;
780 IntRect PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntRect& scrollbarRect) const
782 IntRect rect = scrollbarRect;
783 rect.move(scrollbar.location() - pluginView()->location());
785 return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->pluginElement()->renderer(), rect);
788 IntRect PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntRect& parentRect) const
790 IntRect rect = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->pluginElement()->renderer(), parentRect);
791 rect.move(pluginView()->location() - scrollbar.location());
796 IntPoint PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntPoint& scrollbarPoint) const
798 IntPoint point = scrollbarPoint;
799 point.move(scrollbar.location() - pluginView()->location());
801 return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->pluginElement()->renderer(), point);
804 IntPoint PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntPoint& parentPoint) const
806 IntPoint point = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->pluginElement()->renderer(), parentPoint);
807 point.move(pluginView()->location() - scrollbar.location());
812 bool PDFPlugin::handleScroll(ScrollDirection direction, ScrollGranularity granularity)
814 return scroll(direction, granularity);
817 IntRect PDFPlugin::scrollCornerRect() const
819 if (!m_horizontalScrollbar || !m_verticalScrollbar)
821 if (m_horizontalScrollbar->isOverlayScrollbar()) {
822 ASSERT(m_verticalScrollbar->isOverlayScrollbar());
825 return IntRect(pluginView()->width() - m_verticalScrollbar->width(), pluginView()->height() - m_horizontalScrollbar->height(), m_verticalScrollbar->width(), m_horizontalScrollbar->height());
828 ScrollableArea* PDFPlugin::enclosingScrollableArea() const
830 // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer.
834 IntRect PDFPlugin::scrollableAreaBoundingBox(bool*) const
836 return pluginView()->frameRect();
839 int PDFPlugin::scrollSize(ScrollbarOrientation orientation) const
841 Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
842 return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
845 bool PDFPlugin::isActive() const
847 if (Frame* coreFrame = m_frame->coreFrame()) {
848 if (Page* page = coreFrame->page())
849 return page->focusController().isActive();
855 bool PDFPlugin::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const
857 if (Frame* coreFrame = m_frame->coreFrame()) {
858 if (Page* page = coreFrame->page())
859 return page->settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting();
865 int PDFPlugin::scrollOffset(ScrollbarOrientation orientation) const
867 if (orientation == HorizontalScrollbar)
868 return m_scrollOffset.width();
870 if (orientation == VerticalScrollbar)
871 return m_scrollOffset.height();
873 ASSERT_NOT_REACHED();
877 ScrollPosition PDFPlugin::scrollPosition() const
879 return IntPoint(m_scrollOffset.width(), m_scrollOffset.height());
882 ScrollPosition PDFPlugin::minimumScrollPosition() const
887 ScrollPosition PDFPlugin::maximumScrollPosition() const
889 IntSize scrollbarSpace = scrollbarIntrusion();
891 IntPoint maximumOffset(m_pdfDocumentSize.width() - m_size.width() + scrollbarSpace.width(), m_pdfDocumentSize.height() - m_size.height() + scrollbarSpace.height());
892 maximumOffset.clampNegativeToZero();
893 return maximumOffset;
896 void PDFPlugin::scrollbarStyleChanged(ScrollbarStyle style, bool forceUpdate)
901 // 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.
902 IntPoint newScrollOffset = IntPoint(m_scrollOffset).shrunkTo(maximumScrollPosition());
903 setScrollOffset(newScrollOffset);
905 ScrollableArea::scrollbarStyleChanged(style, forceUpdate);
906 // As size of the content area changes, scrollbars may need to appear or to disappear.
910 void PDFPlugin::addArchiveResource()
912 // FIXME: It's a hack to force add a resource to DocumentLoader. PDF documents should just be fetched as CachedResources.
914 // Add just enough data for context menu handling and web archives to work.
915 NSDictionary* headers = @{ @"Content-Disposition": (NSString *)m_suggestedFilename, @"Content-Type" : @"application/pdf" };
916 RetainPtr<NSURLResponse> response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:m_sourceURL statusCode:200 HTTPVersion:(NSString*)kCFHTTPVersion1_1 headerFields:headers]);
917 ResourceResponse synthesizedResponse(response.get());
919 RefPtr<ArchiveResource> resource = ArchiveResource::create(SharedBuffer::wrapCFData(m_data.get()), m_sourceURL, "application/pdf", String(), String(), synthesizedResponse);
920 pluginView()->frame()->document()->loader()->addArchiveResource(resource.releaseNonNull());
923 static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object)
925 PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
929 static void jsPDFDocFinalize(JSObjectRef object)
931 PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
935 JSValueRef PDFPlugin::jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
937 PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(thisObject));
939 WebFrame* frame = pdfView->m_frame;
941 return JSValueMakeUndefined(ctx);
943 Frame* coreFrame = frame->coreFrame();
945 return JSValueMakeUndefined(ctx);
947 Page* page = coreFrame->page();
949 return JSValueMakeUndefined(ctx);
951 page->chrome().print(*coreFrame);
953 return JSValueMakeUndefined(ctx);
956 JSObjectRef PDFPlugin::makeJSPDFDoc(JSContextRef ctx)
958 static JSStaticFunction jsPDFDocStaticFunctions[] = {
959 { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
963 static JSClassDefinition jsPDFDocClassDefinition = {
965 kJSClassAttributeNone,
969 jsPDFDocStaticFunctions,
970 jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
973 static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition);
975 return JSObjectMake(ctx, jsPDFDocClass, this);
978 static RetainPtr<CFMutableDataRef> convertPostScriptDataToPDF(RetainPtr<CFDataRef> postScriptData)
980 // Convert PostScript to PDF using the Quartz 2D API.
981 // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html
983 CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 };
984 RetainPtr<CGPSConverterRef> converter = adoptCF(CGPSConverterCreate(0, &callbacks, 0));
985 RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateWithCFData(postScriptData.get()));
986 RetainPtr<CFMutableDataRef> pdfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
987 RetainPtr<CGDataConsumerRef> consumer = adoptCF(CGDataConsumerCreateWithCFData(pdfData.get()));
989 CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0);
994 void PDFPlugin::convertPostScriptDataIfNeeded()
999 m_suggestedFilename = String(m_suggestedFilename + ".pdf");
1000 m_data = convertPostScriptDataToPDF(m_data);
1003 void PDFPlugin::pdfDocumentDidLoad()
1005 addArchiveResource();
1007 RetainPtr<PDFDocument> document = adoptNS([[pdfDocumentClass() alloc] initWithData:rawData()]);
1009 setPDFDocument(document);
1011 updatePageAndDeviceScaleFactors();
1013 [m_pdfLayerController setFrameSize:size()];
1014 m_pdfLayerController.get().document = document.get();
1016 if (handlesPageScaleFactor())
1017 pluginView()->setPageScaleFactor([m_pdfLayerController contentScaleFactor], IntPoint());
1019 notifyScrollPositionChanged(IntPoint([m_pdfLayerController scrollPosition]));
1024 runScriptsInPDFDocument();
1026 if ([document isLocked])
1027 createPasswordEntryForm();
1030 void PDFPlugin::streamDidReceiveResponse(uint64_t streamID, const URL&, uint32_t, uint32_t, const String& mimeType, const String&, const String& suggestedFilename)
1032 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
1034 m_suggestedFilename = suggestedFilename;
1036 if (equalIgnoringASCIICase(mimeType, postScriptMIMEType))
1037 m_isPostScript = true;
1040 void PDFPlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
1042 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
1045 m_data = adoptCF(CFDataCreateMutable(0, 0));
1047 CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
1050 void PDFPlugin::streamDidFinishLoading(uint64_t streamID)
1052 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
1054 convertPostScriptDataIfNeeded();
1055 pdfDocumentDidLoad();
1058 void PDFPlugin::streamDidFail(uint64_t streamID, bool wasCancelled)
1060 ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
1065 void PDFPlugin::manualStreamDidReceiveResponse(const URL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFilename)
1067 m_suggestedFilename = suggestedFilename;
1069 if (equalIgnoringASCIICase(mimeType, postScriptMIMEType))
1070 m_isPostScript = true;
1073 void PDFPlugin::manualStreamDidReceiveData(const char* bytes, int length)
1076 m_data = adoptCF(CFDataCreateMutable(0, 0));
1078 CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
1081 void PDFPlugin::manualStreamDidFinishLoading()
1083 convertPostScriptDataIfNeeded();
1084 pdfDocumentDidLoad();
1087 void PDFPlugin::manualStreamDidFail(bool)
1092 void PDFPlugin::runScriptsInPDFDocument()
1094 Vector<RetainPtr<CFStringRef>> scripts;
1095 getAllScriptsInPDFDocument([m_pdfDocument documentRef], scripts);
1097 size_t scriptCount = scripts.size();
1101 JSGlobalContextRef ctx = JSGlobalContextCreate(0);
1102 JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx);
1104 for (size_t i = 0; i < scriptCount; ++i) {
1105 JSStringRef script = JSStringCreateWithCFString(scripts[i].get());
1106 JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0);
1107 JSStringRelease(script);
1110 JSGlobalContextRelease(ctx);
1113 void PDFPlugin::createPasswordEntryForm()
1115 m_passwordField = PDFPluginPasswordField::create(m_pdfLayerController.get(), this);
1116 m_passwordField->attach(m_annotationContainer.get());
1119 void PDFPlugin::attemptToUnlockPDF(const String& password)
1121 [m_pdfLayerController attemptToUnlockWithPassword:password];
1123 if (![pdfDocument() isLocked]) {
1124 m_passwordField = nullptr;
1131 void PDFPlugin::updatePageAndDeviceScaleFactors()
1133 double newScaleFactor = controller()->contentsScaleFactor();
1134 if (!handlesPageScaleFactor())
1135 newScaleFactor *= webFrame()->page()->pageScaleFactor();
1137 if (newScaleFactor != [m_pdfLayerController deviceScaleFactor])
1138 [m_pdfLayerController setDeviceScaleFactor:newScaleFactor];
1141 void PDFPlugin::contentsScaleFactorChanged(float)
1143 updatePageAndDeviceScaleFactors();
1146 void PDFPlugin::computePageBoxes()
1148 size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument documentRef]);
1149 for (size_t i = 0; i < pageCount; ++i) {
1150 CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument documentRef], i + 1);
1153 CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
1154 if (CGRectIsEmpty(box))
1155 box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
1156 m_pageBoxes.append(IntRect(box));
1160 void PDFPlugin::calculateSizes()
1162 if ([pdfDocument() isLocked]) {
1163 setPDFDocumentSize(IntSize(0, 0));
1167 // FIXME: This should come straight from PDFKit.
1170 setPDFDocumentSize(IntSize([m_pdfLayerController contentSizeRespectingZoom]));
1173 bool PDFPlugin::initialize(const Parameters& parameters)
1175 m_sourceURL = parameters.url;
1176 if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty())
1177 controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false);
1179 controller()->didInitializePlugin();
1183 void PDFPlugin::willDetatchRenderer()
1186 if (FrameView* frameView = webFrame()->coreFrame()->view())
1187 frameView->removeScrollableArea(this);
1191 void PDFPlugin::destroy()
1193 m_pdfLayerController.get().delegate = 0;
1196 if (FrameView* frameView = webFrame()->coreFrame()->view())
1197 frameView->removeScrollableArea(this);
1200 m_activeAnnotation = nullptr;
1201 m_annotationContainer = nullptr;
1203 destroyScrollbar(HorizontalScrollbar);
1204 destroyScrollbar(VerticalScrollbar);
1206 [m_scrollCornerLayer removeFromSuperlayer];
1207 [m_contentLayer removeFromSuperlayer];
1210 void PDFPlugin::updateControlTints(GraphicsContext& graphicsContext)
1212 ASSERT(graphicsContext.updatingControlTints());
1214 if (m_horizontalScrollbar)
1215 m_horizontalScrollbar->invalidate();
1216 if (m_verticalScrollbar)
1217 m_verticalScrollbar->invalidate();
1218 invalidateScrollCorner(scrollCornerRect());
1221 void PDFPlugin::paintControlForLayerInContext(CALayer *layer, CGContextRef context)
1223 GraphicsContext graphicsContext(context);
1224 GraphicsContextStateSaver stateSaver(graphicsContext);
1226 graphicsContext.setIsCALayerContext(true);
1228 if (layer == m_scrollCornerLayer) {
1229 IntRect scrollCornerRect = this->scrollCornerRect();
1230 graphicsContext.translate(-scrollCornerRect.x(), -scrollCornerRect.y());
1231 ScrollbarTheme::theme().paintScrollCorner(nullptr, graphicsContext, scrollCornerRect);
1235 Scrollbar* scrollbar = nullptr;
1237 if (layer == m_verticalScrollbarLayer)
1238 scrollbar = verticalScrollbar();
1239 else if (layer == m_horizontalScrollbarLayer)
1240 scrollbar = horizontalScrollbar();
1245 graphicsContext.translate(-scrollbar->x(), -scrollbar->y());
1246 scrollbar->paint(graphicsContext, scrollbar->frameRect());
1249 RefPtr<ShareableBitmap> PDFPlugin::snapshot()
1251 if (size().isEmpty())
1254 float contentsScaleFactor = controller()->contentsScaleFactor();
1255 IntSize backingStoreSize = size();
1256 backingStoreSize.scale(contentsScaleFactor);
1258 RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha);
1259 auto context = bitmap->createGraphicsContext();
1261 context->scale(FloatSize(contentsScaleFactor, -contentsScaleFactor));
1262 context->translate(-m_scrollOffset.width(), -m_pdfDocumentSize.height() + m_scrollOffset.height());
1264 [m_pdfLayerController snapshotInContext:context->platformContext()];
1269 PlatformLayer* PDFPlugin::pluginLayer()
1271 return m_containerLayer.get();
1274 IntPoint PDFPlugin::convertFromPluginToPDFView(const IntPoint& point) const
1276 return IntPoint(point.x(), size().height() - point.y());
1279 IntPoint PDFPlugin::convertFromRootViewToPlugin(const IntPoint& point) const
1281 return m_rootViewToPluginTransform.mapPoint(point);
1284 IntPoint PDFPlugin::convertFromPDFViewToRootView(const IntPoint& point) const
1286 IntPoint pointInPluginCoordinates(point.x(), size().height() - point.y());
1287 return m_rootViewToPluginTransform.inverse().value_or(AffineTransform()).mapPoint(pointInPluginCoordinates);
1290 IntPoint PDFPlugin::convertFromRootViewToPDFView(const IntPoint& point) const
1292 IntPoint pointInPluginCoordinates = m_rootViewToPluginTransform.mapPoint(point);
1293 return IntPoint(pointInPluginCoordinates.x(), size().height() - pointInPluginCoordinates.y());
1296 FloatRect PDFPlugin::convertFromPDFViewToScreen(const FloatRect& rect) const
1298 FrameView* frameView = webFrame()->coreFrame()->view();
1303 FloatRect updatedRect = rect;
1304 updatedRect.setLocation(convertFromPDFViewToRootView(IntPoint(updatedRect.location())));
1305 return webFrame()->coreFrame()->page()->chrome().rootViewToScreen(enclosingIntRect(updatedRect));
1308 IntRect PDFPlugin::boundsOnScreen() const
1310 FrameView* frameView = webFrame()->coreFrame()->view();
1315 FloatRect bounds = FloatRect(FloatPoint(), size());
1316 FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().value_or(AffineTransform()).mapRect(bounds);
1317 return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
1320 void PDFPlugin::geometryDidChange(const IntSize& pluginSize, const IntRect&, const AffineTransform& pluginToRootViewTransform)
1322 if (size() == pluginSize && pluginView()->pageScaleFactor() == [m_pdfLayerController contentScaleFactor])
1325 m_size = pluginSize;
1326 m_rootViewToPluginTransform = pluginToRootViewTransform.inverse().value_or(AffineTransform());
1327 [m_pdfLayerController setFrameSize:pluginSize];
1329 [CATransaction begin];
1330 [CATransaction setDisableActions:YES];
1331 CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
1332 transform = CATransform3DTranslate(transform, 0, -pluginSize.height(), 0);
1334 if (handlesPageScaleFactor()) {
1335 CGFloat magnification = pluginView()->pageScaleFactor() - [m_pdfLayerController contentScaleFactor];
1337 // FIXME: Instead of m_lastMousePositionInPluginCoordinates, we should use the zoom origin from PluginView::setPageScaleFactor.
1339 [m_pdfLayerController magnifyWithMagnification:magnification atPoint:convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates) immediately:NO];
1341 // If we don't handle page scale ourselves, we need to respect our parent page's
1342 // scale, which may have changed.
1343 updatePageAndDeviceScaleFactors();
1349 if (m_activeAnnotation)
1350 m_activeAnnotation->updateGeometry();
1352 [m_contentLayer setSublayerTransform:transform];
1353 [CATransaction commit];
1356 void PDFPlugin::frameDidFinishLoading(uint64_t)
1358 ASSERT_NOT_REACHED();
1361 void PDFPlugin::frameDidFail(uint64_t, bool)
1363 ASSERT_NOT_REACHED();
1366 void PDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&)
1368 ASSERT_NOT_REACHED();
1372 static NSUInteger modifierFlagsFromWebEvent(const WebEvent& event)
1374 return (event.shiftKey() ? NSEventModifierFlagShift : 0)
1375 | (event.controlKey() ? NSEventModifierFlagControl : 0)
1376 | (event.altKey() ? NSEventModifierFlagOption : 0)
1377 | (event.metaKey() ? NSEventModifierFlagCommand : 0);
1380 static bool getEventTypeFromWebEvent(const WebEvent& event, NSEventType& eventType)
1382 switch (event.type()) {
1383 case WebEvent::KeyDown:
1384 eventType = NSEventTypeKeyDown;
1386 case WebEvent::KeyUp:
1387 eventType = NSEventTypeKeyUp;
1389 case WebEvent::MouseDown:
1390 switch (static_cast<const WebMouseEvent&>(event).button()) {
1391 case WebMouseEvent::LeftButton:
1392 eventType = NSEventTypeLeftMouseDown;
1394 case WebMouseEvent::RightButton:
1395 eventType = NSEventTypeRightMouseDown;
1400 case WebEvent::MouseUp:
1401 switch (static_cast<const WebMouseEvent&>(event).button()) {
1402 case WebMouseEvent::LeftButton:
1403 eventType = NSEventTypeLeftMouseUp;
1405 case WebMouseEvent::RightButton:
1406 eventType = NSEventTypeRightMouseUp;
1411 case WebEvent::MouseMove:
1412 switch (static_cast<const WebMouseEvent&>(event).button()) {
1413 case WebMouseEvent::LeftButton:
1414 eventType = NSEventTypeLeftMouseDragged;
1416 case WebMouseEvent::RightButton:
1417 eventType = NSEventTypeRightMouseDragged;
1419 case WebMouseEvent::NoButton:
1420 eventType = NSEventTypeMouseMoved;
1430 NSEvent *PDFPlugin::nsEventForWebMouseEvent(const WebMouseEvent& event)
1432 m_lastMousePositionInPluginCoordinates = convertFromRootViewToPlugin(event.position());
1434 IntPoint positionInPDFViewCoordinates(convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates));
1436 NSEventType eventType;
1438 if (!getEventTypeFromWebEvent(event, eventType))
1441 NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1443 return [NSEvent mouseEventWithType:eventType location:positionInPDFViewCoordinates modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0];
1446 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
1447 void PDFPlugin::updateCursor(const WebMouseEvent& event, UpdateCursorMode mode)
1449 HitTestResult hitTestResult = None;
1451 PDFSelection *selectionUnderMouse = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(event.position())];
1452 if (selectionUnderMouse && [[selectionUnderMouse string] length])
1453 hitTestResult = Text;
1455 if (hitTestResult == m_lastHitTestResult && mode == UpdateIfNeeded)
1458 webFrame()->page()->send(Messages::WebPageProxy::SetCursor(hitTestResult == Text ? iBeamCursor() : pointerCursor()));
1459 m_lastHitTestResult = hitTestResult;
1463 bool PDFPlugin::handleMouseEvent(const WebMouseEvent& event)
1465 PlatformMouseEvent platformEvent = platform(event);
1466 IntPoint mousePosition = convertFromRootViewToPlugin(event.position());
1468 m_lastMouseEvent = event;
1470 RefPtr<Scrollbar> targetScrollbar;
1471 RefPtr<Scrollbar> targetScrollbarForLastMousePosition;
1473 if (m_verticalScrollbarLayer) {
1474 IntRect verticalScrollbarFrame(m_verticalScrollbarLayer.get().frame);
1475 if (verticalScrollbarFrame.contains(mousePosition))
1476 targetScrollbar = verticalScrollbar();
1477 if (verticalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1478 targetScrollbarForLastMousePosition = verticalScrollbar();
1481 if (m_horizontalScrollbarLayer) {
1482 IntRect horizontalScrollbarFrame(m_horizontalScrollbarLayer.get().frame);
1483 if (horizontalScrollbarFrame.contains(mousePosition))
1484 targetScrollbar = horizontalScrollbar();
1485 if (horizontalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1486 targetScrollbarForLastMousePosition = horizontalScrollbar();
1489 if (m_scrollCornerLayer && IntRect(m_scrollCornerLayer.get().frame).contains(mousePosition))
1492 if ([pdfDocument() isLocked])
1495 // Right-clicks and Control-clicks always call handleContextMenuEvent as well.
1496 if (event.button() == WebMouseEvent::RightButton || (event.button() == WebMouseEvent::LeftButton && event.controlKey()))
1499 NSEvent *nsEvent = nsEventForWebMouseEvent(event);
1501 switch (event.type()) {
1502 case WebEvent::MouseMove:
1503 mouseMovedInContentArea();
1504 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
1505 updateCursor(event);
1508 if (targetScrollbar) {
1509 if (!targetScrollbarForLastMousePosition) {
1510 targetScrollbar->mouseEntered();
1513 return targetScrollbar->mouseMoved(platformEvent);
1516 if (!targetScrollbar && targetScrollbarForLastMousePosition)
1517 targetScrollbarForLastMousePosition->mouseExited();
1519 switch (event.button()) {
1520 case WebMouseEvent::LeftButton:
1521 [m_pdfLayerController mouseDragged:nsEvent];
1523 case WebMouseEvent::RightButton:
1524 case WebMouseEvent::MiddleButton:
1526 case WebMouseEvent::NoButton:
1527 [m_pdfLayerController mouseMoved:nsEvent];
1531 case WebEvent::MouseDown:
1532 switch (event.button()) {
1533 case WebMouseEvent::LeftButton:
1534 if (targetScrollbar)
1535 return targetScrollbar->mouseDown(platformEvent);
1537 [m_pdfLayerController mouseDown:nsEvent];
1539 case WebMouseEvent::RightButton:
1540 [m_pdfLayerController rightMouseDown:nsEvent];
1542 case WebMouseEvent::MiddleButton:
1543 case WebMouseEvent::NoButton:
1547 case WebEvent::MouseUp:
1548 switch (event.button()) {
1549 case WebMouseEvent::LeftButton:
1550 if (targetScrollbar)
1551 return targetScrollbar->mouseUp(platformEvent);
1553 [m_pdfLayerController mouseUp:nsEvent];
1555 case WebMouseEvent::RightButton:
1556 case WebMouseEvent::MiddleButton:
1557 case WebMouseEvent::NoButton:
1568 bool PDFPlugin::handleMouseEnterEvent(const WebMouseEvent& event)
1570 mouseEnteredContentArea();
1571 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
1572 updateCursor(event, ForceUpdate);
1577 bool PDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&)
1579 mouseExitedContentArea();
1583 bool PDFPlugin::showContextMenuAtPoint(const IntPoint& point)
1585 FrameView* frameView = webFrame()->coreFrame()->view();
1586 IntPoint contentsPoint = frameView->contentsToRootView(point);
1587 WebMouseEvent event(WebEvent::MouseDown, WebMouseEvent::RightButton, contentsPoint, contentsPoint, 0, 0, 0, 1, static_cast<WebEvent::Modifiers>(0), monotonicallyIncreasingTime(), WebCore::ForceAtClick);
1588 return handleContextMenuEvent(event);
1591 bool PDFPlugin::handleContextMenuEvent(const WebMouseEvent& event)
1593 FrameView* frameView = webFrame()->coreFrame()->view();
1594 IntPoint point = frameView->contentsToScreen(IntRect(frameView->windowToContents(event.position()), IntSize())).location();
1596 if (NSMenu *nsMenu = [m_pdfLayerController menuForEvent:nsEventForWebMouseEvent(event)]) {
1597 WKPopupContextMenu(nsMenu, point);
1604 bool PDFPlugin::handleKeyboardEvent(const WebKeyboardEvent& event)
1606 NSEventType eventType;
1608 if (!getEventTypeFromWebEvent(event, eventType))
1611 NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1613 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()];
1615 switch (event.type()) {
1616 case WebEvent::KeyDown:
1617 return [m_pdfLayerController keyDown:fakeEvent];
1625 bool PDFPlugin::handleEditingCommand(const String& commandName, const String& argument)
1627 if (commandName == "copy")
1628 [m_pdfLayerController copySelection];
1629 else if (commandName == "selectAll")
1630 [m_pdfLayerController selectAll];
1631 else if (commandName == "takeFindStringFromSelection") {
1632 NSString *string = [m_pdfLayerController currentSelection].string;
1633 #pragma clang diagnostic push
1634 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1636 writeItemsToPasteboard(NSFindPboard, @[ [string dataUsingEncoding:NSUTF8StringEncoding] ], @[ NSPasteboardTypeString ]);
1637 #pragma clang diagnostic pop
1643 bool PDFPlugin::isEditingCommandEnabled(const String& commandName)
1645 if (commandName == "copy" || commandName == "takeFindStringFromSelection")
1646 return [m_pdfLayerController currentSelection];
1648 if (commandName == "selectAll")
1654 void PDFPlugin::setScrollOffset(const ScrollOffset& offset)
1656 m_scrollOffset = IntSize(offset.x(), offset.y());
1658 [CATransaction begin];
1659 [m_pdfLayerController setScrollPosition:offset];
1661 if (m_activeAnnotation)
1662 m_activeAnnotation->updateGeometry();
1664 [CATransaction commit];
1667 void PDFPlugin::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
1669 if (&scrollbar == horizontalScrollbar())
1670 [m_horizontalScrollbarLayer setNeedsDisplay];
1671 else if (&scrollbar == verticalScrollbar())
1672 [m_verticalScrollbarLayer setNeedsDisplay];
1675 void PDFPlugin::invalidateScrollCornerRect(const IntRect& rect)
1677 [m_scrollCornerLayer setNeedsDisplay];
1680 bool PDFPlugin::isFullFramePlugin() const
1682 // <object> or <embed> plugins will appear to be in their parent frame, so we have to
1683 // check whether our frame's widget is exactly our PluginView.
1684 Document* document = webFrame()->coreFrame()->document();
1685 return document->isPluginDocument() && static_cast<PluginDocument*>(document)->pluginWidget() == pluginView();
1688 bool PDFPlugin::handlesPageScaleFactor() const
1690 return webFrame()->isMainFrame() && isFullFramePlugin();
1693 void PDFPlugin::clickedLink(NSURL *url)
1696 if (protocolIsJavaScript(coreURL))
1699 Frame* frame = webFrame()->coreFrame();
1701 RefPtr<Event> coreEvent;
1702 if (m_lastMouseEvent.type() != WebEvent::NoType)
1703 coreEvent = MouseEvent::create(eventNames().clickEvent, frame->document()->defaultView(), platform(m_lastMouseEvent), 0, 0);
1705 frame->loader().urlSelected(coreURL, emptyString(), coreEvent.get(), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, ShouldOpenExternalURLsPolicy::ShouldAllow);
1708 void PDFPlugin::setActiveAnnotation(PDFAnnotation *annotation)
1710 if (!supportsForms())
1713 if (m_activeAnnotation)
1714 m_activeAnnotation->commit();
1717 #pragma clang diagnostic push
1718 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1719 if ([annotation isKindOfClass:pdfAnnotationTextWidgetClass()] && static_cast<PDFAnnotationTextWidget *>(annotation).isReadOnly) {
1720 m_activeAnnotation = nullptr;
1723 #pragma clang diagnostic pop
1725 m_activeAnnotation = PDFPluginAnnotation::create(annotation, m_pdfLayerController.get(), this);
1726 m_activeAnnotation->attach(m_annotationContainer.get());
1728 m_activeAnnotation = nullptr;
1731 bool PDFPlugin::supportsForms()
1733 // 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.
1734 return isFullFramePlugin();
1737 void PDFPlugin::notifyContentScaleFactorChanged(CGFloat scaleFactor)
1739 if (handlesPageScaleFactor())
1740 pluginView()->setPageScaleFactor(scaleFactor, IntPoint());
1746 void PDFPlugin::notifyDisplayModeChanged(int)
1752 RefPtr<SharedBuffer> PDFPlugin::liveResourceData() const
1754 NSData *pdfData = liveData();
1759 return SharedBuffer::wrapNSData(pdfData);
1762 bool PDFPlugin::pluginHandlesContentOffsetForAccessibilityHitTest() const
1764 // The PDF plugin handles the scroll view offset natively as part of the layer conversions.
1765 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
1773 void PDFPlugin::saveToPDF()
1775 // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1776 // PDFViewController does an NSBeep(), but that seems insufficient.
1780 NSData *data = liveData();
1781 webFrame()->page()->savePDFToFileInDownloadsFolder(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length]);
1784 void PDFPlugin::openWithNativeApplication()
1786 if (!m_temporaryPDFUUID) {
1787 // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1788 // PDFViewController does an NSBeep(), but that seems insufficient.
1792 NSData *data = liveData();
1794 m_temporaryPDFUUID = WebCore::createCanonicalUUIDString();
1795 ASSERT(m_temporaryPDFUUID);
1797 webFrame()->page()->savePDFToTemporaryFolderAndOpenWithNativeApplication(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length], m_temporaryPDFUUID);
1801 webFrame()->page()->send(Messages::WebPageProxy::OpenPDFFromTemporaryFolderWithNativeApplication(m_temporaryPDFUUID));
1804 void PDFPlugin::writeItemsToPasteboard(NSString *pasteboardName, NSArray *items, NSArray *types)
1806 Vector<String> pasteboardTypes;
1808 for (NSString *type in types)
1809 pasteboardTypes.append(type);
1811 uint64_t newChangeCount;
1812 auto& webProcess = WebProcess::singleton();
1813 webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardTypes(pasteboardName, pasteboardTypes),
1814 Messages::WebPasteboardProxy::SetPasteboardTypes::Reply(newChangeCount), 0);
1816 for (NSUInteger i = 0, count = items.count; i < count; ++i) {
1817 NSString *type = [types objectAtIndex:i];
1818 NSData *data = [items objectAtIndex:i];
1820 // We don't expect the data for any items to be empty, but aren't completely sure.
1821 // Avoid crashing in the SharedMemory constructor in release builds if we're wrong.
1822 ASSERT(data.length);
1826 if ([type isEqualToString:NSStringPboardType] || [type isEqualToString:NSPasteboardTypeString]) {
1827 RetainPtr<NSString> plainTextString = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
1828 webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardStringForType(pasteboardName, type, plainTextString.get()), Messages::WebPasteboardProxy::SetPasteboardStringForType::Reply(newChangeCount), 0);
1830 RefPtr<SharedBuffer> buffer = SharedBuffer::wrapNSData(data);
1835 SharedMemory::Handle handle;
1836 RefPtr<SharedMemory> sharedMemory = SharedMemory::allocate(buffer->size());
1837 memcpy(sharedMemory->data(), buffer->data(), buffer->size());
1838 sharedMemory->createHandle(handle, SharedMemory::Protection::ReadOnly);
1839 webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardBufferForType(pasteboardName, type, handle, buffer->size()), Messages::WebPasteboardProxy::SetPasteboardBufferForType::Reply(newChangeCount), 0);
1844 void PDFPlugin::showDefinitionForAttributedString(NSAttributedString *string, CGPoint point)
1846 DictionaryPopupInfo dictionaryPopupInfo;
1847 dictionaryPopupInfo.origin = convertFromPDFViewToRootView(IntPoint(point));
1848 dictionaryPopupInfo.attributedString = string;
1850 webFrame()->page()->send(Messages::WebPageProxy::DidPerformDictionaryLookup(dictionaryPopupInfo));
1853 unsigned PDFPlugin::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1855 if (!target.length())
1858 int nsOptions = (options & FindOptionsCaseInsensitive) ? NSCaseInsensitiveSearch : 0;
1860 return [[pdfDocument() findString:target withOptions:nsOptions] count];
1863 PDFSelection *PDFPlugin::nextMatchForString(const String& target, BOOL searchForward, BOOL caseSensitive, BOOL wrapSearch, PDFSelection *initialSelection, BOOL startInSelection)
1865 if (!target.length())
1868 NSStringCompareOptions options = 0;
1870 options |= NSBackwardsSearch;
1873 options |= NSCaseInsensitiveSearch;
1875 PDFDocument *document = pdfDocument().get();
1877 RetainPtr<PDFSelection> selectionForInitialSearch = adoptNS([initialSelection copy]);
1878 if (startInSelection) {
1879 // Initially we want to include the selected text in the search. So we must modify the starting search
1880 // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before
1881 // the current selection (if searching forwards) or after (if searching backwards).
1882 int initialSelectionLength = [[initialSelection string] length];
1883 if (searchForward) {
1884 [selectionForInitialSearch extendSelectionAtStart:1];
1885 [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
1887 [selectionForInitialSearch extendSelectionAtEnd:1];
1888 [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
1892 PDFSelection *foundSelection = [document findString:target fromSelection:selectionForInitialSearch.get() withOptions:options];
1894 // If we first searched in the selection, and we found the selection, search again from just past the selection.
1895 if (startInSelection && [foundSelection isEqual:initialSelection])
1896 foundSelection = [document findString:target fromSelection:initialSelection withOptions:options];
1898 if (!foundSelection && wrapSearch) {
1899 auto emptySelection = adoptNS([[pdfSelectionClass() alloc] initWithDocument:document]);
1900 foundSelection = [document findString:target fromSelection:emptySelection.get() withOptions:options];
1903 return foundSelection;
1906 bool PDFPlugin::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1908 BOOL searchForward = !(options & FindOptionsBackwards);
1909 BOOL caseSensitive = !(options & FindOptionsCaseInsensitive);
1910 BOOL wrapSearch = options & FindOptionsWrapAround;
1912 unsigned matchCount;
1913 if (!maxMatchCount) {
1914 // If the max was zero, any result means we exceeded the max. We can skip computing the actual count.
1915 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1917 matchCount = countFindMatches(target, options, maxMatchCount);
1918 if (matchCount > maxMatchCount)
1919 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1922 if (target.isEmpty()) {
1923 PDFSelection* searchSelection = [m_pdfLayerController searchSelection];
1924 [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1925 [m_pdfLayerController setSearchSelection:searchSelection];
1926 m_lastFoundString = emptyString();
1930 if (m_lastFoundString == target) {
1931 PDFSelection *selection = nextMatchForString(target, searchForward, caseSensitive, wrapSearch, [m_pdfLayerController searchSelection], NO);
1935 [m_pdfLayerController setSearchSelection:selection];
1936 [m_pdfLayerController gotoSelection:selection];
1938 [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1939 m_lastFoundString = target;
1942 return matchCount > 0;
1945 bool PDFPlugin::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
1947 IntPoint localPoint = convertFromRootViewToPlugin(roundedIntPoint(point));
1948 PDFSelection* lookupSelection = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(localPoint)];
1950 if ([[lookupSelection string] length])
1951 [m_pdfLayerController searchInDictionaryWithSelection:lookupSelection];
1956 void PDFPlugin::focusNextAnnotation()
1958 [m_pdfLayerController activateNextAnnotation:false];
1961 void PDFPlugin::focusPreviousAnnotation()
1963 [m_pdfLayerController activateNextAnnotation:true];
1966 void PDFPlugin::notifySelectionChanged(PDFSelection *)
1968 webFrame()->page()->didChangeSelection();
1971 static const Cursor& pdfLayerControllerCursorTypeToCursor(PDFLayerControllerCursorType type)
1974 case kPDFLayerControllerCursorTypeHand:
1975 return handCursor();
1976 case kPDFLayerControllerCursorTypeIBeam:
1977 return iBeamCursor();
1978 case kPDFLayerControllerCursorTypePointer:
1980 return pointerCursor();
1984 void PDFPlugin::notifyCursorChanged(uint64_t type)
1986 webFrame()->page()->send(Messages::WebPageProxy::SetCursor(pdfLayerControllerCursorTypeToCursor(static_cast<PDFLayerControllerCursorType>(type))));
1989 String PDFPlugin::getSelectionString() const
1991 return [[m_pdfLayerController currentSelection] string];
1994 String PDFPlugin::getSelectionForWordAtPoint(const WebCore::FloatPoint& point) const
1996 IntPoint pointInView = convertFromPluginToPDFView(convertFromRootViewToPlugin(roundedIntPoint(point)));
1997 PDFSelection *selectionForWord = [m_pdfLayerController getSelectionForWordAtPoint:pointInView];
1998 [m_pdfLayerController setCurrentSelection:selectionForWord];
2000 return [selectionForWord string];
2003 bool PDFPlugin::existingSelectionContainsPoint(const WebCore::FloatPoint& locationInViewCoordinates) const
2005 PDFSelection *currentSelection = [m_pdfLayerController currentSelection];
2006 if (!currentSelection)
2009 IntPoint pointInPDFView = convertFromPluginToPDFView(convertFromRootViewToPlugin(roundedIntPoint(locationInViewCoordinates)));
2010 PDFSelection *selectionForWord = [m_pdfLayerController getSelectionForWordAtPoint:pointInPDFView];
2012 NSUInteger currentPageIndex = [m_pdfLayerController currentPageIndex];
2014 NSArray *selectionRects = [m_pdfLayerController rectsForSelectionInLayoutSpace:currentSelection];
2015 if (!selectionRects || !selectionRects.count)
2018 if (currentPageIndex >= selectionRects.count)
2019 currentPageIndex = selectionRects.count - 1;
2021 NSArray *wordSelectionRects = [m_pdfLayerController rectsForSelectionInLayoutSpace:selectionForWord];
2022 if (!wordSelectionRects || !wordSelectionRects.count)
2025 NSValue *selectionBounds = [selectionRects objectAtIndex:currentPageIndex];
2026 NSValue *wordSelectionBounds = [wordSelectionRects objectAtIndex:0];
2028 NSRect selectionBoundsRect = selectionBounds.rectValue;
2029 NSRect wordSelectionBoundsRect = wordSelectionBounds.rectValue;
2030 return NSIntersectsRect(wordSelectionBoundsRect, selectionBoundsRect);
2033 static NSPoint pointInLayoutSpaceForPointInWindowSpace(PDFLayerController* pdfLayerController, NSPoint pointInView)
2035 CGPoint point = NSPointToCGPoint(pointInView);
2036 CGPoint scrollOffset = [pdfLayerController scrollPosition];
2037 CGFloat scaleFactor = [pdfLayerController contentScaleFactor];
2039 scrollOffset.y = [pdfLayerController contentSizeRespectingZoom].height - NSRectToCGRect([pdfLayerController frame]).size.height - scrollOffset.y;
2041 CGPoint newPoint = CGPointMake(scrollOffset.x + point.x, scrollOffset.y + point.y);
2042 newPoint.x /= scaleFactor;
2043 newPoint.y /= scaleFactor;
2044 return NSPointFromCGPoint(newPoint);
2047 String PDFPlugin::lookupTextAtLocation(const WebCore::FloatPoint& locationInViewCoordinates, WebHitTestResultData& data, PDFSelection **selectionPtr, NSDictionary **options) const
2049 PDFSelection*& selection = *selectionPtr;
2051 selection = [m_pdfLayerController currentSelection];
2052 if (existingSelectionContainsPoint(locationInViewCoordinates))
2053 return selection.string;
2055 IntPoint pointInPDFView = convertFromPluginToPDFView(convertFromRootViewToPlugin(roundedIntPoint(locationInViewCoordinates)));
2056 selection = [m_pdfLayerController getSelectionForWordAtPoint:pointInPDFView];
2060 NSPoint pointInLayoutSpace = pointInLayoutSpaceForPointInWindowSpace(m_pdfLayerController.get(), pointInPDFView);
2062 PDFPage *currentPage = [[m_pdfLayerController layout] pageNearestPoint:pointInLayoutSpace currentPage:[m_pdfLayerController currentPage]];
2064 NSPoint pointInPageSpace = [[m_pdfLayerController layout] convertPoint:pointInLayoutSpace toPage:currentPage forScaleFactor:1.0];
2066 for (PDFAnnotation *annotation in currentPage.annotations) {
2067 if (![annotation isKindOfClass:pdfAnnotationLinkClass()])
2070 NSRect bounds = annotation.bounds;
2071 if (!NSPointInRect(pointInPageSpace, bounds))
2074 #pragma clang diagnostic push
2075 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2076 PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
2077 #pragma clang diagnostic pop
2078 NSURL *url = linkAnnotation.URL;
2082 data.absoluteLinkURL = url.absoluteString;
2083 data.linkLabel = selection.string;
2084 return selection.string;
2087 NSString *lookupText = DictionaryLookup::stringForPDFSelection(selection, options);
2088 if (!lookupText || !lookupText.length)
2091 [m_pdfLayerController setCurrentSelection:selection];
2095 static NSRect rectInViewSpaceForRectInLayoutSpace(PDFLayerController* pdfLayerController, NSRect layoutSpaceRect)
2097 CGRect newRect = NSRectToCGRect(layoutSpaceRect);
2098 CGFloat scaleFactor = pdfLayerController.contentScaleFactor;
2099 CGPoint scrollOffset = pdfLayerController.scrollPosition;
2101 scrollOffset.y = pdfLayerController.contentSizeRespectingZoom.height - NSRectToCGRect(pdfLayerController.frame).size.height - scrollOffset.y;
2103 newRect.origin.x *= scaleFactor;
2104 newRect.origin.y *= scaleFactor;
2105 newRect.size.width *= scaleFactor;
2106 newRect.size.height *= scaleFactor;
2108 newRect.origin.x -= scrollOffset.x;
2109 newRect.origin.y -= scrollOffset.y;
2111 return NSRectFromCGRect(newRect);
2114 WebCore::AXObjectCache* PDFPlugin::axObjectCache() const
2116 return webFrame()->coreFrame()->document()->axObjectCache();
2119 WebCore::FloatRect PDFPlugin::rectForSelectionInRootView(PDFSelection *selection) const
2121 PDFPage *currentPage = nil;
2122 NSArray* pages = selection.pages;
2124 currentPage = (PDFPage *)[pages objectAtIndex:0];
2127 currentPage = [m_pdfLayerController currentPage];
2129 NSRect rectInPageSpace = [selection boundsForPage:currentPage];
2130 NSRect rectInLayoutSpace = [[m_pdfLayerController layout] convertRect:rectInPageSpace fromPage:currentPage forScaleFactor:1.0];
2131 NSRect rectInView = rectInViewSpaceForRectInLayoutSpace(m_pdfLayerController.get(), rectInLayoutSpace);
2133 rectInView.origin = convertFromPDFViewToRootView(IntPoint(rectInView.origin));
2135 return WebCore::FloatRect(rectInView);
2138 CGFloat PDFPlugin::scaleFactor() const
2140 return [m_pdfLayerController contentScaleFactor];
2143 void PDFPlugin::performWebSearch(NSString *string)
2145 webFrame()->page()->send(Messages::WebPageProxy::SearchTheWeb(string));
2148 void PDFPlugin::performSpotlightSearch(NSString *string)
2150 webFrame()->page()->send(Messages::WebPageProxy::SearchWithSpotlight(string));
2153 bool PDFPlugin::handleWheelEvent(const WebWheelEvent& event)
2155 PDFDisplayMode displayMode = [m_pdfLayerController displayMode];
2157 if (displayMode == kPDFDisplaySinglePageContinuous || displayMode == kPDFDisplayTwoUpContinuous)
2158 return ScrollableArea::handleWheelEvent(platform(event));
2160 NSUInteger currentPageIndex = [m_pdfLayerController currentPageIndex];
2161 bool inFirstPage = !currentPageIndex;
2162 bool inLastPage = [m_pdfLayerController lastPageIndex] == currentPageIndex;
2164 bool atScrollTop = !scrollPosition().y();
2165 bool atScrollBottom = scrollPosition().y() == maximumScrollPosition().y();
2167 bool inMomentumScroll = event.momentumPhase() != WebWheelEvent::PhaseNone;
2169 int scrollMagnitudeThresholdForPageFlip = defaultScrollMagnitudeThresholdForPageFlip;
2171 // Imprecise input devices should have a lower threshold so that "clicky" scroll wheels can flip pages.
2172 if (!event.hasPreciseScrollingDeltas())
2173 scrollMagnitudeThresholdForPageFlip = 0;
2175 if (atScrollBottom && !inLastPage && event.delta().height() < 0) {
2176 if (event.delta().height() <= -scrollMagnitudeThresholdForPageFlip && !inMomentumScroll)
2177 [m_pdfLayerController gotoNextPage];
2181 if (atScrollTop && !inFirstPage && event.delta().height() > 0) {
2182 if (event.delta().height() >= scrollMagnitudeThresholdForPageFlip && !inMomentumScroll) {
2183 [CATransaction begin];
2184 [m_pdfLayerController gotoPreviousPage];
2185 scrollToOffsetWithoutAnimation(maximumScrollPosition());
2186 [CATransaction commit];
2191 return ScrollableArea::handleWheelEvent(platform(event));
2194 NSData *PDFPlugin::liveData() const
2196 if (m_activeAnnotation)
2197 m_activeAnnotation->commit();
2199 // Save data straight from the resource instead of PDFKit if the document is
2200 // untouched by the user, so that PDFs which PDFKit can't display will still be downloadable.
2201 if (m_pdfDocumentWasMutated)
2202 return [m_pdfDocument dataRepresentation];
2207 id PDFPlugin::accessibilityAssociatedPluginParentForElement(WebCore::Element* element) const
2209 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
2210 if (!m_activeAnnotation)
2213 if (m_activeAnnotation->element() != element)
2216 return [m_activeAnnotation->annotation() accessibilityNode];
2222 NSObject *PDFPlugin::accessibilityObject() const
2224 return m_accessibilityObject.get();
2227 } // namespace WebKit
2229 #endif // ENABLE(PDFKIT_PLUGIN)