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