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