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