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