Implement the updated port/area-based Scroll Snap Module Level 1 Spec
[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) && !USE(DEPRECATED_PDF_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/ArchiveResource.h>
54 #import <WebCore/Chrome.h>
55 #import <WebCore/Cursor.h>
56 #import <WebCore/DictionaryLookup.h>
57 #import <WebCore/DocumentLoader.h>
58 #import <WebCore/FocusController.h>
59 #import <WebCore/FormState.h>
60 #import <WebCore/Frame.h>
61 #import <WebCore/FrameLoader.h>
62 #import <WebCore/FrameView.h>
63 #import <WebCore/GraphicsContext.h>
64 #import <WebCore/GraphicsLayerCA.h>
65 #import <WebCore/HTMLElement.h>
66 #import <WebCore/HTMLFormElement.h>
67 #import <WebCore/HTMLPlugInElement.h>
68 #import <WebCore/LocalizedStrings.h>
69 #import <WebCore/MainFrame.h>
70 #import <WebCore/MouseEvent.h>
71 #import <WebCore/Page.h>
72 #import <WebCore/PageOverlayController.h>
73 #import <WebCore/Pasteboard.h>
74 #import <WebCore/PlatformCAAnimationCocoa.h>
75 #import <WebCore/PluginData.h>
76 #import <WebCore/PluginDocument.h>
77 #import <WebCore/RenderBoxModelObject.h>
78 #import <WebCore/Settings.h>
79 #import <WebCore/UUID.h>
80 #import <WebKitSystemInterface.h>
81 #import <wtf/CurrentTime.h>
82
83 using namespace WebCore;
84
85 static const char* annotationStyle =
86 "body { "
87 "    background-color: rgb(146, 146, 146) !important;"
88 "} "
89 "#passwordContainer {"
90 "    display: -webkit-box; "
91 "    -webkit-box-align: center; "
92 "    -webkit-box-pack: center; "
93 "    position: fixed; "
94 "    top: 0; "
95 "    left: 0; "
96 "    right: 0; "
97 "    bottom: 0; "
98 "} "
99 ".annotation { "
100 "    position: absolute; "
101 "    pointer-events: auto; "
102 "} "
103 "textarea.annotation { "
104 "    resize: none; "
105 "} "
106 "input.annotation[type='password'] { "
107 "    position: static; "
108 "    width: 200px; "
109 "    margin-top: 100px; "
110 "} ";
111
112 const double zoomButtonScaleMultiplier = 1.18920;
113
114 @interface WKPDFPluginAccessibilityObject : NSObject {
115     PDFLayerController *_pdfLayerController;
116     NSObject *_parent;
117     WebKit::PDFPlugin* _pdfPlugin;
118 }
119
120 @property (assign) PDFLayerController *pdfLayerController;
121 @property (assign) NSObject *parent;
122 @property (assign) WebKit::PDFPlugin* pdfPlugin;
123
124 - (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin;
125
126 @end
127
128 @implementation WKPDFPluginAccessibilityObject
129
130 @synthesize pdfLayerController=_pdfLayerController;
131 @synthesize parent=_parent;
132 @synthesize pdfPlugin=_pdfPlugin;
133
134 - (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
135 {
136     if (!(self = [super init]))
137         return nil;
138
139     _pdfPlugin = plugin;
140
141     return self;
142 }
143
144 - (BOOL)accessibilityIsIgnored
145 {
146     return NO;
147 }
148
149 - (id)accessibilityAttributeValue:(NSString *)attribute
150 {
151     if ([attribute isEqualToString:NSAccessibilityParentAttribute])
152         return _parent;
153     if ([attribute isEqualToString:NSAccessibilityValueAttribute])
154         return [_pdfLayerController accessibilityValueAttribute];
155     if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
156         return [_pdfLayerController accessibilitySelectedTextAttribute];
157     if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
158         return [_pdfLayerController accessibilitySelectedTextRangeAttribute];
159     if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
160         return [_pdfLayerController accessibilityNumberOfCharactersAttribute];
161     if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
162         return [_pdfLayerController accessibilityVisibleCharacterRangeAttribute];
163     if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
164         return [_parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
165     if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
166         return [_pdfLayerController accessibilityRoleAttribute];
167     if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
168         return [_pdfLayerController accessibilityRoleDescriptionAttribute];
169     if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
170         return [_parent accessibilityAttributeValue:NSAccessibilityWindowAttribute];
171     if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
172         return [NSValue valueWithSize:_pdfPlugin->boundsOnScreen().size()];
173     if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
174         return [_parent accessibilityAttributeValue:NSAccessibilityFocusedAttribute];
175     if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
176         return [_parent accessibilityAttributeValue:NSAccessibilityEnabledAttribute];
177     if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
178         return [NSValue valueWithPoint:_pdfPlugin->boundsOnScreen().location()];
179
180     return 0;
181 }
182
183 - (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter
184 {
185     if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
186         NSRect boundsInPDFViewCoordinates = [[_pdfLayerController accessibilityBoundsForRangeAttributeForParameter:parameter] rectValue];
187         NSRect boundsInScreenCoordinates = _pdfPlugin->convertFromPDFViewToScreen(boundsInPDFViewCoordinates);
188         return [NSValue valueWithRect:boundsInScreenCoordinates];
189     }
190
191     if ([attribute isEqualToString:NSAccessibilityLineForIndexParameterizedAttribute])
192         return [_pdfLayerController accessibilityLineForIndexAttributeForParameter:parameter];
193     if ([attribute isEqualToString:NSAccessibilityRangeForLineParameterizedAttribute])
194         return [_pdfLayerController accessibilityRangeForLineAttributeForParameter:parameter];
195     if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute])
196         return [_pdfLayerController accessibilityStringForRangeAttributeForParameter:parameter];
197
198     return 0;
199 }
200
201 - (CPReadingModel *)readingModel
202 {
203     return [_pdfLayerController readingModel];
204 }
205
206 - (NSArray *)accessibilityAttributeNames
207 {
208     static NSArray *attributeNames = 0;
209
210     if (!attributeNames) {
211         attributeNames = @[NSAccessibilityValueAttribute,
212             NSAccessibilitySelectedTextAttribute,
213             NSAccessibilitySelectedTextRangeAttribute,
214             NSAccessibilityNumberOfCharactersAttribute,
215             NSAccessibilityVisibleCharacterRangeAttribute,
216             NSAccessibilityParentAttribute,
217             NSAccessibilityRoleAttribute,
218             NSAccessibilityWindowAttribute,
219             NSAccessibilityTopLevelUIElementAttribute,
220             NSAccessibilityRoleDescriptionAttribute,
221             NSAccessibilitySizeAttribute,
222             NSAccessibilityFocusedAttribute,
223             NSAccessibilityEnabledAttribute,
224             NSAccessibilityPositionAttribute];
225         [attributeNames retain];
226     }
227
228     return attributeNames;
229 }
230
231 - (NSArray *)accessibilityActionNames
232 {
233     static NSArray *actionNames = 0;
234     
235     if (!actionNames)
236         actionNames = [[NSArray arrayWithObject:NSAccessibilityShowMenuAction] retain];
237     
238     return actionNames;
239 }
240
241 - (void)accessibilityPerformAction:(NSString *)action
242 {
243     if ([action isEqualToString:NSAccessibilityShowMenuAction])
244         _pdfPlugin->showContextMenuAtPoint(IntRect(IntPoint(), _pdfPlugin->size()).center());
245 }
246
247 - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute
248 {
249     return [_pdfLayerController accessibilityIsAttributeSettable:attribute];
250 }
251
252 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
253 {
254     return [_pdfLayerController accessibilitySetValue:value forAttribute:attribute];
255 }
256
257 - (NSArray *)accessibilityParameterizedAttributeNames
258 {
259     return [_pdfLayerController accessibilityParameterizedAttributeNames];
260 }
261
262 - (id)accessibilityFocusedUIElement
263 {
264     return self;
265 }
266
267 - (id)accessibilityHitTest:(NSPoint)point
268 {
269     return self;
270 }
271
272 @end
273
274 @interface WKPDFLayerControllerDelegate : NSObject<PDFLayerControllerDelegate> {
275     WebKit::PDFPlugin* _pdfPlugin;
276 }
277
278 @property (assign) WebKit::PDFPlugin* pdfPlugin;
279
280 @end
281
282 @implementation WKPDFLayerControllerDelegate
283
284 @synthesize pdfPlugin=_pdfPlugin;
285
286 - (id)initWithPDFPlugin:(WebKit::PDFPlugin *)plugin
287 {
288     if (!(self = [super init]))
289         return nil;
290     
291     _pdfPlugin = plugin;
292     
293     return self;
294 }
295
296 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController scrollToPoint:(CGPoint)newPosition
297 {
298     _pdfPlugin->scrollToPoint(IntPoint(newPosition));
299 }
300
301 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController copyItems:(NSArray *)items withTypes:(NSArray *)types
302 {
303     _pdfPlugin->writeItemsToPasteboard(NSGeneralPboard, items, types);
304 }
305
306 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController showDefinitionForAttributedString:(NSAttributedString *)string atPoint:(CGPoint)point
307 {
308     _pdfPlugin->showDefinitionForAttributedString(string, point);
309 }
310
311 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController performWebSearchForString:(NSString *)string
312 {
313     _pdfPlugin->performWebSearch(string);
314 }
315
316 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController performSpotlightSearchForString:(NSString *)string
317 {
318     _pdfPlugin->performSpotlightSearch(string);
319 }
320
321 - (void)pdfLayerControllerOpenWithNativeApplication:(PDFLayerController *)pdfLayerController
322 {
323     _pdfPlugin->openWithNativeApplication();
324 }
325
326 - (void)pdfLayerControllerSaveToPDF:(PDFLayerController *)pdfLayerController
327 {
328     _pdfPlugin->saveToPDF();
329 }
330
331 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didClickLinkWithURL:(NSURL *)url
332 {
333     _pdfPlugin->clickedLink(url);
334 }
335
336 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeActiveAnnotation:(PDFAnnotation *)annotation
337 {
338     _pdfPlugin->setActiveAnnotation(annotation);
339 }
340
341 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeDisplayMode:(int)mode
342 {
343     _pdfPlugin->notifyDisplayModeChanged(mode);
344 }
345
346 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController didChangeSelection:(PDFSelection *)selection
347 {
348     _pdfPlugin->notifySelectionChanged(selection);
349 }
350
351 - (void)pdfLayerController:(PDFLayerController *)pdfLayerController invalidateRect:(CGRect)rect
352 {
353     _pdfPlugin->invalidatePDFRect(enclosingIntRect(rect));
354 }
355
356 - (void)pdfLayerControllerInvalidateHUD:(PDFLayerController *)pdfLayerController
357 {
358     _pdfPlugin->invalidateHUD();
359 }
360
361 - (void)pdfLayerControllerZoomIn:(PDFLayerController *)pdfLayerController
362 {
363     _pdfPlugin->zoomIn();
364 }
365
366 - (void)pdfLayerControllerZoomOut:(PDFLayerController *)pdfLayerController
367 {
368     _pdfPlugin->zoomOut();
369 }
370
371 @end
372
373 @interface WKPDFHUDAnimationDelegate : NSObject {
374     std::function<void (bool)> _completionHandler;
375 }
376 @end
377
378 @implementation WKPDFHUDAnimationDelegate
379
380 - (id)initWithAnimationCompletionHandler:(std::function<void (bool)>)completionHandler
381 {
382     if (!(self = [super init]))
383         return nil;
384
385     _completionHandler = WTFMove(completionHandler);
386
387     return self;
388 }
389
390 - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
391 {
392     _completionHandler(flag);
393 }
394
395 @end
396
397 @interface PDFViewLayout
398 - (NSPoint)convertPoint:(NSPoint)point toPage:(PDFPage *)page forScaleFactor:(CGFloat)scaleFactor;
399 - (NSRect)convertRect:(NSRect)rect fromPage:(PDFPage *) page forScaleFactor:(CGFloat) scaleFactor;
400 - (PDFPage *)pageNearestPoint:(NSPoint)point currentPage:(PDFPage *)currentPage;
401 @end
402
403 static const char* postScriptMIMEType = "application/postscript";
404 const uint64_t pdfDocumentRequestID = 1; // PluginController supports loading multiple streams, but we only need one for PDF.
405
406 static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values)
407 {
408     CGPDFArrayRef names;
409     if (CGPDFDictionaryGetArray(subtree, "Names", &names)) {
410         size_t nameCount = CGPDFArrayGetCount(names) / 2;
411         for (size_t i = 0; i < nameCount; ++i) {
412             CGPDFObjectRef object;
413             CGPDFArrayGetObject(names, 2 * i + 1, &object);
414             values.append(object);
415         }
416         return;
417     }
418
419     CGPDFArrayRef kids;
420     if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids))
421         return;
422
423     size_t kidCount = CGPDFArrayGetCount(kids);
424     for (size_t i = 0; i < kidCount; ++i) {
425         CGPDFDictionaryRef kid;
426         if (!CGPDFArrayGetDictionary(kids, i, &kid))
427             continue;
428         appendValuesInPDFNameSubtreeToVector(kid, values);
429     }
430 }
431
432 static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues)
433 {
434     appendValuesInPDFNameSubtreeToVector(tree, allValues);
435 }
436
437 static void getAllScriptsInPDFDocument(CGPDFDocumentRef pdfDocument, Vector<RetainPtr<CFStringRef>>& scripts)
438 {
439     if (!pdfDocument)
440         return;
441
442     CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument);
443     if (!pdfCatalog)
444         return;
445
446     // Get the dictionary of all document-level name trees.
447     CGPDFDictionaryRef namesDictionary;
448     if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary))
449         return;
450
451     // Get the document-level "JavaScript" name tree.
452     CGPDFDictionaryRef javaScriptNameTree;
453     if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree))
454         return;
455
456     // The names are arbitrary. We are only interested in the values.
457     Vector<CGPDFObjectRef> objects;
458     getAllValuesInPDFNameTree(javaScriptNameTree, objects);
459     size_t objectCount = objects.size();
460
461     for (size_t i = 0; i < objectCount; ++i) {
462         CGPDFDictionaryRef javaScriptAction;
463         if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction))
464             continue;
465
466         // A JavaScript action must have an action type of "JavaScript".
467         const char* actionType;
468         if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript"))
469             continue;
470
471         const UInt8* bytes = 0;
472         CFIndex length;
473         CGPDFStreamRef stream;
474         CGPDFStringRef string;
475         RetainPtr<CFDataRef> data;
476         if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) {
477             CGPDFDataFormat format;
478             data = adoptCF(CGPDFStreamCopyData(stream, &format));
479             if (!data)
480                 continue;
481             bytes = CFDataGetBytePtr(data.get());
482             length = CFDataGetLength(data.get());
483         } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) {
484             bytes = CGPDFStringGetBytePtr(string);
485             length = CGPDFStringGetLength(string);
486         }
487         if (!bytes)
488             continue;
489
490         CFStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? kCFStringEncodingUnicode : kCFStringEncodingUTF8;
491         RetainPtr<CFStringRef> script = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, bytes, length, encoding, true));
492         if (!script)
493             continue;
494         
495         scripts.append(script);
496     }
497 }
498
499 namespace WebKit {
500
501 using namespace HTMLNames;
502
503 Ref<PDFPlugin> PDFPlugin::create(WebFrame* frame)
504 {
505     return adoptRef(*new PDFPlugin(frame));
506 }
507
508 PDFPlugin::PDFPlugin(WebFrame* frame)
509     : Plugin(PDFPluginType)
510     , m_frame(frame)
511     , m_pdfLayerController(adoptNS([[pdfLayerControllerClass() alloc] init]))
512     , m_pdfLayerControllerDelegate(adoptNS([[WKPDFLayerControllerDelegate alloc] initWithPDFPlugin:this]))
513     , m_HUD(*this)
514 {
515     [m_pdfLayerController setDelegate:m_pdfLayerControllerDelegate.get()];
516
517     if (supportsForms()) {
518         Document* document = webFrame()->coreFrame()->document();
519
520         Ref<Element> annotationStyleElement = document->createElement(styleTag, false);
521         annotationStyleElement->setTextContent(annotationStyle, ASSERT_NO_EXCEPTION);
522
523         document->bodyOrFrameset()->appendChild(annotationStyleElement.get());
524     }
525
526     m_accessibilityObject = adoptNS([[WKPDFPluginAccessibilityObject alloc] initWithPDFPlugin:this]);
527     [m_accessibilityObject setPdfLayerController:m_pdfLayerController.get()];
528     [m_accessibilityObject setParent:webFrame()->page()->accessibilityRemoteObject()];
529 }
530
531 PDFPlugin::~PDFPlugin()
532 {
533 }
534
535 PluginInfo PDFPlugin::pluginInfo()
536 {
537     PluginInfo info;
538     info.name = builtInPDFPluginName();
539     info.isApplicationPlugin = true;
540     info.clientLoadPolicy = PluginLoadClientPolicyUndefined;
541
542     MimeClassInfo pdfMimeClassInfo;
543     pdfMimeClassInfo.type = "application/pdf";
544     pdfMimeClassInfo.desc = pdfDocumentTypeDescription();
545     pdfMimeClassInfo.extensions.append("pdf");
546     info.mimes.append(pdfMimeClassInfo);
547
548     MimeClassInfo textPDFMimeClassInfo;
549     textPDFMimeClassInfo.type = "text/pdf";
550     textPDFMimeClassInfo.desc = pdfDocumentTypeDescription();
551     textPDFMimeClassInfo.extensions.append("pdf");
552     info.mimes.append(textPDFMimeClassInfo);
553
554     MimeClassInfo postScriptMimeClassInfo;
555     postScriptMimeClassInfo.type = postScriptMIMEType;
556     postScriptMimeClassInfo.desc = postScriptDocumentTypeDescription();
557     postScriptMimeClassInfo.extensions.append("ps");
558     info.mimes.append(postScriptMimeClassInfo);
559     
560     return info;
561 }
562
563 PluginView* PDFPlugin::pluginView()
564 {
565     return static_cast<PluginView*>(controller());
566 }
567
568 const PluginView* PDFPlugin::pluginView() const
569 {
570     return static_cast<const PluginView*>(controller());
571 }
572
573 void PDFPlugin::addArchiveResource()
574 {
575     // FIXME: It's a hack to force add a resource to DocumentLoader. PDF documents should just be fetched as CachedResources.
576
577     // Add just enough data for context menu handling and web archives to work.
578     NSDictionary* headers = @{ @"Content-Disposition": (NSString *)m_suggestedFilename, @"Content-Type" : @"application/pdf" };
579     RetainPtr<NSURLResponse> response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:m_sourceURL statusCode:200 HTTPVersion:(NSString*)kCFHTTPVersion1_1 headerFields:headers]);
580     ResourceResponse synthesizedResponse(response.get());
581
582     RefPtr<ArchiveResource> resource = ArchiveResource::create(SharedBuffer::wrapCFData(m_data.get()), m_sourceURL, "application/pdf", String(), String(), synthesizedResponse);
583     pluginView()->frame()->document()->loader()->addArchiveResource(resource.release());
584 }
585
586 static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object)
587 {
588     PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
589     pdfView->ref();
590 }
591
592 static void jsPDFDocFinalize(JSObjectRef object)
593 {
594     PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
595     pdfView->deref();
596 }
597
598 JSValueRef PDFPlugin::jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
599 {
600     PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(thisObject));
601
602     WebFrame* frame = pdfView->m_frame;
603     if (!frame)
604         return JSValueMakeUndefined(ctx);
605
606     Frame* coreFrame = frame->coreFrame();
607     if (!coreFrame)
608         return JSValueMakeUndefined(ctx);
609
610     Page* page = coreFrame->page();
611     if (!page)
612         return JSValueMakeUndefined(ctx);
613
614     page->chrome().print(coreFrame);
615
616     return JSValueMakeUndefined(ctx);
617 }
618
619 JSObjectRef PDFPlugin::makeJSPDFDoc(JSContextRef ctx)
620 {
621     static JSStaticFunction jsPDFDocStaticFunctions[] = {
622         { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
623         { 0, 0, 0 },
624     };
625
626     static JSClassDefinition jsPDFDocClassDefinition = {
627         0,
628         kJSClassAttributeNone,
629         "Doc",
630         0,
631         0,
632         jsPDFDocStaticFunctions,
633         jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
634     };
635
636     static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition);
637
638     return JSObjectMake(ctx, jsPDFDocClass, this);
639 }
640
641 static RetainPtr<CFMutableDataRef> convertPostScriptDataToPDF(RetainPtr<CFDataRef> postScriptData)
642 {
643     // Convert PostScript to PDF using the Quartz 2D API.
644     // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html
645
646     CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 };
647     RetainPtr<CGPSConverterRef> converter = adoptCF(CGPSConverterCreate(0, &callbacks, 0));
648     RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateWithCFData(postScriptData.get()));
649     RetainPtr<CFMutableDataRef> pdfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
650     RetainPtr<CGDataConsumerRef> consumer = adoptCF(CGDataConsumerCreateWithCFData(pdfData.get()));
651     
652     CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0);
653     
654     return pdfData;
655 }
656
657 void PDFPlugin::convertPostScriptDataIfNeeded()
658 {
659     if (!m_isPostScript)
660         return;
661
662     m_suggestedFilename = String(m_suggestedFilename + ".pdf");
663     m_data = convertPostScriptDataToPDF(m_data);
664 }
665
666 void PDFPlugin::pdfDocumentDidLoad()
667 {
668     addArchiveResource();
669     
670     RetainPtr<PDFDocument> document = adoptNS([[pdfDocumentClass() alloc] initWithData:rawData()]);
671
672     setPDFDocument(document);
673
674     [m_pdfLayerController setFrameSize:webFrame()->coreFrame()->view()->visibleContentRect().size()]; /// ???
675     [m_pdfLayerController setDocument:document.get()];
676
677     calculateSizes();
678
679     runScriptsInPDFDocument();
680
681     if (isLocked())
682         createPasswordEntryForm();
683
684     m_HUD.setVisible(!isLocked(), HUD::AnimateVisibilityTransition::No);
685 }
686
687 bool PDFPlugin::isLocked() const
688 {
689     return [m_pdfDocument isLocked];
690 }
691
692 void PDFPlugin::streamDidReceiveResponse(uint64_t streamID, const URL&, uint32_t, uint32_t, const String& mimeType, const String&, const String& suggestedFilename)
693 {
694     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
695
696     m_suggestedFilename = suggestedFilename;
697
698     if (equalIgnoringASCIICase(mimeType, postScriptMIMEType))
699         m_isPostScript = true;
700 }
701
702 void PDFPlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
703 {
704     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
705
706     if (!m_data)
707         m_data = adoptCF(CFDataCreateMutable(0, 0));
708
709     CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
710 }
711
712 void PDFPlugin::streamDidFinishLoading(uint64_t streamID)
713 {
714     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
715
716     convertPostScriptDataIfNeeded();
717     pdfDocumentDidLoad();
718 }
719
720 void PDFPlugin::streamDidFail(uint64_t streamID, bool wasCancelled)
721 {
722     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
723
724     m_data.clear();
725 }
726
727 void PDFPlugin::manualStreamDidReceiveResponse(const URL& responseURL, uint32_t streamLength,  uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFilename)
728 {
729     m_suggestedFilename = suggestedFilename;
730
731     if (equalIgnoringASCIICase(mimeType, postScriptMIMEType))
732         m_isPostScript = true;
733 }
734
735 void PDFPlugin::manualStreamDidReceiveData(const char* bytes, int length)
736 {
737     if (!m_data)
738         m_data = adoptCF(CFDataCreateMutable(0, 0));
739
740     CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
741 }
742
743 void PDFPlugin::manualStreamDidFinishLoading()
744 {
745     convertPostScriptDataIfNeeded();
746     pdfDocumentDidLoad();
747 }
748
749 void PDFPlugin::manualStreamDidFail(bool)
750 {
751     m_data.clear();
752 }
753
754 void PDFPlugin::runScriptsInPDFDocument()
755 {
756     Vector<RetainPtr<CFStringRef>> scripts;
757     getAllScriptsInPDFDocument([m_pdfDocument documentRef], scripts);
758
759     size_t scriptCount = scripts.size();
760     if (!scriptCount)
761         return;
762
763     JSGlobalContextRef ctx = JSGlobalContextCreate(0);
764     JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx);
765
766     for (size_t i = 0; i < scriptCount; ++i) {
767         JSStringRef script = JSStringCreateWithCFString(scripts[i].get());
768         JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0);
769         JSStringRelease(script);
770     }
771     
772     JSGlobalContextRelease(ctx);
773 }
774
775 void PDFPlugin::invalidatePDFRect(IntRect rect)
776 {
777     Widget* widget = pluginView();
778
779     // FIXME: One of the conversion functions should do this (with the flipping).
780     IntRect invalidRect = rect;
781     invalidRect.setY(m_pdfDocumentSize.height() - invalidRect.y() - invalidRect.height());
782
783     widget->invalidateRect(invalidRect);
784 }
785
786 void PDFPlugin::invalidateHUD()
787 {
788     m_HUD.invalidate();
789 }
790
791 void PDFPlugin::createPasswordEntryForm()
792 {
793     if (!supportsForms())
794         return;
795
796     Document* document = webFrame()->coreFrame()->document();
797     m_passwordContainer = document->createElement(divTag, false);
798     m_passwordContainer->setAttributeWithoutSynchronization(idAttr, AtomicString("passwordContainer", AtomicString::ConstructFromLiteral));
799
800     m_passwordField = PDFPluginPasswordField::create(m_pdfLayerController.get(), this);
801     m_passwordField->attach(m_passwordContainer.get());
802     document->bodyOrFrameset()->appendChild(*m_passwordContainer);
803 }
804
805 void PDFPlugin::attemptToUnlockPDF(const String& password)
806 {
807     [m_pdfLayerController attemptToUnlockWithPassword:password];
808
809     if (!isLocked()) {
810         m_passwordContainer = nullptr;
811         m_passwordField = nullptr;
812
813         calculateSizes();
814
815         m_HUD.setVisible(true, HUD::AnimateVisibilityTransition::Yes);
816     }
817 }
818
819 void PDFPlugin::calculateSizes()
820 {
821     setPDFDocumentSize(isLocked() ? IntSize() : IntSize([m_pdfLayerController contentSizeRespectingZoom]));
822
823     // We have to asynchronously update styles because we could be inside layout.
824     RefPtr<PDFPlugin> reffedThis = this;
825     dispatch_async(dispatch_get_main_queue(), [reffedThis] {
826         reffedThis->didCalculateSizes();
827     });
828 }
829
830 void PDFPlugin::didCalculateSizes()
831 {
832     HTMLPlugInElement* pluginElement = downcast<PluginDocument>(*webFrame()->coreFrame()->document()).pluginElement();
833
834     if (isLocked()) {
835         pluginElement->setInlineStyleProperty(CSSPropertyWidth, 100, CSSPrimitiveValue::CSS_PERCENTAGE);
836         pluginElement->setInlineStyleProperty(CSSPropertyHeight, 100, CSSPrimitiveValue::CSS_PERCENTAGE);
837         return;
838     }
839
840     pluginElement->setInlineStyleProperty(CSSPropertyWidth, m_pdfDocumentSize.width(), CSSPrimitiveValue::CSS_PX);
841     pluginElement->setInlineStyleProperty(CSSPropertyHeight, m_pdfDocumentSize.height(), CSSPrimitiveValue::CSS_PX);
842
843     // FIXME: Adopt the new scroll snap specification.
844 }
845
846 bool PDFPlugin::initialize(const Parameters& parameters)
847 {
848     m_sourceURL = parameters.url;
849     if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty())
850         controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false);
851
852     controller()->didInitializePlugin();
853     return true;
854 }
855
856 void PDFPlugin::destroy()
857 {
858     [m_pdfLayerController setDelegate:nil];
859
860     m_activeAnnotation = nullptr;
861 }
862
863 void PDFPlugin::paint(GraphicsContext& context, const IntRect& dirtyRectInWindowCoordinates)
864 {
865     context.scale(FloatSize(1, -1));
866     context.translate(0, -m_pdfDocumentSize.height());
867     [m_pdfLayerController drawInContext:context.platformContext()];
868 }
869
870 RefPtr<ShareableBitmap> PDFPlugin::snapshot()
871 {
872     return nullptr;
873 }
874
875 IntPoint PDFPlugin::convertFromPluginToPDFView(const IntPoint& point) const
876 {
877     return IntPoint(point.x(), m_size.height() - point.y());
878 }
879
880 IntPoint PDFPlugin::convertFromRootViewToPlugin(const IntPoint& point) const
881 {
882     return m_rootViewToPluginTransform.mapPoint(point);
883 }
884
885 IntPoint PDFPlugin::convertFromPDFViewToRootView(const IntPoint& point) const
886 {
887     IntPoint pointInPluginCoordinates(point.x(), m_size.height() - point.y());
888     return m_pluginToRootViewTransform.mapPoint(pointInPluginCoordinates);
889 }
890
891 FloatRect PDFPlugin::convertFromPDFViewToScreen(const FloatRect& rect) const
892 {
893     FrameView* frameView = webFrame()->coreFrame()->view();
894
895     if (!frameView)
896         return FloatRect();
897
898     FloatPoint originInPluginCoordinates(rect.x(), m_size.height() - rect.y() - rect.height());
899     FloatRect rectInRootViewCoordinates = m_pluginToRootViewTransform.mapRect(FloatRect(originInPluginCoordinates, rect.size()));
900
901     return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
902 }
903
904 IntRect PDFPlugin::boundsOnScreen() const
905 {
906     FrameView* frameView = webFrame()->coreFrame()->view();
907
908     if (!frameView)
909         return IntRect();
910
911     FloatRect bounds = FloatRect(FloatPoint(), size());
912     FloatRect rectInRootViewCoordinates = m_pluginToRootViewTransform.mapRect(bounds);
913     return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
914 }
915
916 void PDFPlugin::geometryDidChange(const IntSize& pluginSize, const IntRect&, const AffineTransform& pluginToRootViewTransform)
917 {
918     m_pluginToRootViewTransform = pluginToRootViewTransform;
919     m_rootViewToPluginTransform = pluginToRootViewTransform.inverse().value_or(AffineTransform());
920     m_size = pluginSize;
921
922     FrameView* frameView = webFrame()->coreFrame()->view();
923
924     [m_pdfLayerController setFrameSize:frameView->frameRect().size()];
925     [m_pdfLayerController setVisibleRect:frameView->visibleContentRect()];
926
927     calculateSizes();
928
929     if (m_activeAnnotation)
930         m_activeAnnotation->updateGeometry();
931 }
932
933 void PDFPlugin::frameDidFinishLoading(uint64_t)
934 {
935     ASSERT_NOT_REACHED();
936 }
937
938 void PDFPlugin::frameDidFail(uint64_t, bool)
939 {
940     ASSERT_NOT_REACHED();
941 }
942
943 void PDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&)
944 {
945     ASSERT_NOT_REACHED();
946 }
947
948 static NSUInteger modifierFlagsFromWebEvent(const WebEvent& event)
949 {
950     return (event.shiftKey() ? NSEventModifierFlagShift : 0)
951         | (event.controlKey() ? NSEventModifierFlagControl : 0)
952         | (event.altKey() ? NSEventModifierFlagOption : 0)
953         | (event.metaKey() ? NSEventModifierFlagCommand : 0);
954 }
955     
956 static bool getEventTypeFromWebEvent(const WebEvent& event, NSEventType& eventType)
957 {
958     switch (event.type()) {
959     case WebEvent::MouseDown:
960         switch (static_cast<const WebMouseEvent&>(event).button()) {
961         case WebMouseEvent::LeftButton:
962             eventType = NSEventTypeLeftMouseDown;
963             return true;
964         case WebMouseEvent::RightButton:
965             eventType = NSEventTypeRightMouseDown;
966             return true;
967         default:
968             return false;
969         }
970     case WebEvent::MouseUp:
971         switch (static_cast<const WebMouseEvent&>(event).button()) {
972         case WebMouseEvent::LeftButton:
973             eventType = NSEventTypeLeftMouseUp;
974             return true;
975         case WebMouseEvent::RightButton:
976             eventType = NSEventTypeRightMouseUp;
977             return true;
978         default:
979             return false;
980         }
981     case WebEvent::MouseMove:
982         switch (static_cast<const WebMouseEvent&>(event).button()) {
983         case WebMouseEvent::LeftButton:
984             eventType = NSEventTypeLeftMouseDragged;
985             return true;
986         case WebMouseEvent::RightButton:
987             eventType = NSEventTypeRightMouseDragged;
988             return true;
989         case WebMouseEvent::NoButton:
990             eventType = NSEventTypeMouseMoved;
991             return true;
992         default:
993             return false;
994         }
995     default:
996         return false;
997     }
998 }
999     
1000 NSEvent *PDFPlugin::nsEventForWebMouseEvent(const WebMouseEvent& event)
1001 {
1002     m_lastMousePositionInPluginCoordinates = convertFromRootViewToPlugin(event.position());
1003     IntPoint positionInPDFViewCoordinates(convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates));
1004
1005     NSEventType eventType;
1006
1007     if (!getEventTypeFromWebEvent(event, eventType))
1008         return nullptr;
1009
1010     NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1011
1012     return [NSEvent mouseEventWithType:eventType location:positionInPDFViewCoordinates modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0];
1013 }
1014
1015 void PDFPlugin::updateCursor(const WebMouseEvent& event, UpdateCursor mode)
1016 {
1017     // FIXME: Should have a hand for links, and something special for annotations.
1018
1019     HitTestResult hitTestResult = HitTestResult::None;
1020
1021     IntPoint positionInPDFViewCoordinates(convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates));
1022     if (m_HUD.containsPointInRootView(event.position()))
1023         hitTestResult = HitTestResult::HUD;
1024     else {
1025         PDFSelection *selectionUnderMouse = [m_pdfLayerController getSelectionForWordAtPoint:positionInPDFViewCoordinates];
1026         if (selectionUnderMouse && [[selectionUnderMouse string] length])
1027             hitTestResult = HitTestResult::Text;
1028     }
1029
1030     if (hitTestResult == m_lastHitTestResult && mode == UpdateCursor::IfNeeded)
1031         return;
1032
1033     const Cursor& cursor = [hitTestResult] {
1034         switch (hitTestResult) {
1035         case HitTestResult::None:
1036             return pointerCursor();
1037         case HitTestResult::Text:
1038             return iBeamCursor();
1039         case HitTestResult::HUD:
1040             return pointerCursor();
1041         };
1042     }();
1043
1044     webFrame()->page()->send(Messages::WebPageProxy::SetCursor(cursor));
1045     m_lastHitTestResult = hitTestResult;
1046 }
1047
1048 bool PDFPlugin::handleMouseEvent(const WebMouseEvent& event)
1049 {
1050     PlatformMouseEvent platformEvent = platform(event);
1051
1052     m_lastMouseEvent = event;
1053
1054     if (isLocked())
1055         return false;
1056
1057     // Right-clicks and Control-clicks always call handleContextMenuEvent as well.
1058     if (event.button() == WebMouseEvent::RightButton || (event.button() == WebMouseEvent::LeftButton && event.controlKey()))
1059         return true;
1060
1061     if (event.button() != WebMouseEvent::LeftButton)
1062         return false;
1063
1064     NSEvent *nsEvent = nsEventForWebMouseEvent(event);
1065
1066     switch (event.type()) {
1067     case WebEvent::MouseMove:
1068         updateCursor(event);
1069         [m_pdfLayerController mouseDragged:nsEvent];
1070         return true;
1071     case WebEvent::MouseDown:
1072         [m_pdfLayerController mouseDown:nsEvent];
1073         return true;
1074     case WebEvent::MouseUp:
1075         [m_pdfLayerController mouseUp:nsEvent];
1076         return true;
1077     default:
1078         break;
1079     }
1080
1081     return false;
1082 }
1083
1084 bool PDFPlugin::handleMouseEnterEvent(const WebMouseEvent& event)
1085 {
1086     updateCursor(event, UpdateCursor::Force);
1087     return false;
1088 }
1089
1090 bool PDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&)
1091 {
1092     return false;
1093 }
1094     
1095 bool PDFPlugin::showContextMenuAtPoint(const IntPoint& point)
1096 {
1097     FrameView* frameView = webFrame()->coreFrame()->view();
1098     IntPoint contentsPoint = frameView->contentsToRootView(point);
1099     WebMouseEvent event(WebEvent::MouseDown, WebMouseEvent::RightButton, contentsPoint, contentsPoint, 0, 0, 0, 1, static_cast<WebEvent::Modifiers>(0), monotonicallyIncreasingTime(), ForceAtClick);
1100     return handleContextMenuEvent(event);
1101 }
1102
1103 bool PDFPlugin::handleContextMenuEvent(const WebMouseEvent& event)
1104 {
1105     FrameView* frameView = webFrame()->coreFrame()->view();
1106     IntPoint point = frameView->contentsToScreen(IntRect(frameView->windowToContents(event.position()), IntSize())).location();
1107     
1108     if (NSMenu *nsMenu = [m_pdfLayerController menuForEvent:nsEventForWebMouseEvent(event)]) {
1109         WKPopupContextMenu(nsMenu, point);
1110         return true;
1111     }
1112     
1113     return false;
1114 }
1115     
1116 bool PDFPlugin::handleEditingCommand(const String& commandName, const String& argument)
1117 {
1118     if (commandName == "copy")
1119         [m_pdfLayerController copySelection];
1120     else if (commandName == "selectAll")
1121         [m_pdfLayerController selectAll];
1122     else if (commandName == "takeFindStringFromSelection") {
1123         NSString *string = [m_pdfLayerController currentSelection].string;
1124         if (string.length)
1125             writeItemsToPasteboard(NSFindPboard, @[ [string dataUsingEncoding:NSUTF8StringEncoding] ], @[ NSPasteboardTypeString ]);
1126     }
1127
1128     return true;
1129 }
1130
1131 bool PDFPlugin::isEditingCommandEnabled(const String& commandName)
1132 {
1133     if (commandName == "copy" || commandName == "takeFindStringFromSelection")
1134         return [m_pdfLayerController currentSelection];
1135
1136     if (commandName == "selectAll")
1137         return true;
1138
1139     return false;
1140 }
1141
1142 bool PDFPlugin::isFullFramePlugin() const
1143 {
1144     // <object> or <embed> plugins will appear to be in their parent frame, so we have to
1145     // check whether our frame's widget is exactly our PluginView.
1146     Document* document = webFrame()->coreFrame()->document();
1147     return document->isPluginDocument() && static_cast<PluginDocument*>(document)->pluginWidget() == pluginView();
1148 }
1149
1150 void PDFPlugin::clickedLink(NSURL *url)
1151 {
1152     URL coreURL = url;
1153     if (protocolIsJavaScript(coreURL))
1154         return;
1155
1156     Frame* frame = webFrame()->coreFrame();
1157
1158     RefPtr<Event> coreEvent;
1159     if (m_lastMouseEvent.type() != WebEvent::NoType)
1160         coreEvent = MouseEvent::create(eventNames().clickEvent, frame->document()->defaultView(), platform(m_lastMouseEvent), 0, 0);
1161
1162     frame->loader().urlSelected(coreURL, emptyString(), coreEvent.get(), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, ShouldOpenExternalURLsPolicy::ShouldAllow);
1163 }
1164
1165 void PDFPlugin::setActiveAnnotation(PDFAnnotation *annotation)
1166 {
1167     if (!supportsForms())
1168         return;
1169
1170     if (m_activeAnnotation)
1171         m_activeAnnotation->commit();
1172
1173     if (annotation) {
1174         if ([annotation isKindOfClass:pdfAnnotationTextWidgetClass()] && static_cast<PDFAnnotationTextWidget *>(annotation).isReadOnly) {
1175             m_activeAnnotation = nullptr;
1176             return;
1177         }
1178
1179         m_activeAnnotation = PDFPluginAnnotation::create(annotation, m_pdfLayerController.get(), this);
1180         Document* document = webFrame()->coreFrame()->document();
1181         m_activeAnnotation->attach(document->bodyOrFrameset());
1182     } else
1183         m_activeAnnotation = nullptr;
1184 }
1185
1186 bool PDFPlugin::supportsForms()
1187 {
1188     // 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.
1189     return isFullFramePlugin();
1190 }
1191
1192 void PDFPlugin::notifyDisplayModeChanged(int mode)
1193 {
1194     m_usingContinuousMode = (mode == kPDFDisplaySinglePageContinuous || mode == kPDFDisplayTwoUpContinuous);
1195     calculateSizes();
1196 }
1197
1198 RefPtr<SharedBuffer> PDFPlugin::liveResourceData() const
1199 {
1200     NSData *pdfData = liveData();
1201
1202     if (!pdfData)
1203         return nullptr;
1204
1205     return SharedBuffer::wrapNSData(pdfData);
1206 }
1207
1208 void PDFPlugin::saveToPDF()
1209 {
1210     // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1211     // PDFViewController does an NSBeep(), but that seems insufficient.
1212     if (!pdfDocument())
1213         return;
1214
1215     NSData *data = liveData();
1216     webFrame()->page()->savePDFToFileInDownloadsFolder(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length]);
1217 }
1218
1219 void PDFPlugin::openWithNativeApplication()
1220 {
1221     if (!m_temporaryPDFUUID) {
1222         // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1223         // PDFViewController does an NSBeep(), but that seems insufficient.
1224         if (!pdfDocument())
1225             return;
1226
1227         NSData *data = liveData();
1228
1229         m_temporaryPDFUUID = createCanonicalUUIDString();
1230         ASSERT(m_temporaryPDFUUID);
1231
1232         webFrame()->page()->savePDFToTemporaryFolderAndOpenWithNativeApplication(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length], m_temporaryPDFUUID);
1233         return;
1234     }
1235
1236     webFrame()->page()->send(Messages::WebPageProxy::OpenPDFFromTemporaryFolderWithNativeApplication(m_temporaryPDFUUID));
1237 }
1238
1239 void PDFPlugin::writeItemsToPasteboard(NSString *pasteboardName, NSArray *items, NSArray *types)
1240 {
1241     Vector<String> pasteboardTypes;
1242
1243     for (NSString *type in types)
1244         pasteboardTypes.append(type);
1245
1246     uint64_t newChangeCount;
1247     auto& webProcess = WebProcess::singleton();
1248     webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardTypes(pasteboardName, pasteboardTypes),
1249         Messages::WebPasteboardProxy::SetPasteboardTypes::Reply(newChangeCount), 0);
1250
1251     for (NSUInteger i = 0, count = items.count; i < count; ++i) {
1252         NSString *type = [types objectAtIndex:i];
1253         NSData *data = [items objectAtIndex:i];
1254
1255         // We don't expect the data for any items to be empty, but aren't completely sure.
1256         // Avoid crashing in the SharedMemory constructor in release builds if we're wrong.
1257         ASSERT(data.length);
1258         if (!data.length)
1259             continue;
1260
1261         if ([type isEqualToString:NSStringPboardType] || [type isEqualToString:NSPasteboardTypeString]) {
1262             RetainPtr<NSString> plainTextString = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
1263             webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardStringForType(pasteboardName, type, plainTextString.get()), Messages::WebPasteboardProxy::SetPasteboardStringForType::Reply(newChangeCount), 0);
1264         } else {
1265             RefPtr<SharedBuffer> buffer = SharedBuffer::wrapNSData(data);
1266
1267             if (!buffer)
1268                 continue;
1269
1270             SharedMemory::Handle handle;
1271             RefPtr<SharedMemory> sharedMemory = SharedMemory::allocate(buffer->size());
1272             memcpy(sharedMemory->data(), buffer->data(), buffer->size());
1273             sharedMemory->createHandle(handle, SharedMemory::Protection::ReadOnly);
1274             webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardBufferForType(pasteboardName, type, handle, buffer->size()), Messages::WebPasteboardProxy::SetPasteboardBufferForType::Reply(newChangeCount), 0);
1275         }
1276     }
1277 }
1278
1279 void PDFPlugin::showDefinitionForAttributedString(NSAttributedString *string, CGPoint point)
1280 {
1281     DictionaryPopupInfo dictionaryPopupInfo;
1282     dictionaryPopupInfo.origin = convertFromPDFViewToRootView(IntPoint(point));
1283     dictionaryPopupInfo.attributedString = string;
1284
1285     webFrame()->page()->send(Messages::WebPageProxy::DidPerformDictionaryLookup(dictionaryPopupInfo));
1286 }
1287
1288 unsigned PDFPlugin::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1289 {
1290     if (!target.length())
1291         return 0;
1292
1293     int nsOptions = (options & FindOptionsCaseInsensitive) ? NSCaseInsensitiveSearch : 0;
1294
1295     return [[pdfDocument() findString:target withOptions:nsOptions] count];
1296 }
1297
1298 PDFSelection *PDFPlugin::nextMatchForString(const String& target, BOOL searchForward, BOOL caseSensitive, BOOL wrapSearch, PDFSelection *initialSelection, BOOL startInSelection)
1299 {
1300     if (!target.length())
1301         return nil;
1302
1303     NSStringCompareOptions options = 0;
1304     if (!searchForward)
1305         options |= NSBackwardsSearch;
1306
1307     if (!caseSensitive)
1308         options |= NSCaseInsensitiveSearch;
1309
1310     PDFDocument *document = pdfDocument().get();
1311
1312     PDFSelection *selectionForInitialSearch = [initialSelection copy];
1313     if (startInSelection) {
1314         // Initially we want to include the selected text in the search. So we must modify the starting search
1315         // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before
1316         // the current selection (if searching forwards) or after (if searching backwards).
1317         int initialSelectionLength = [[initialSelection string] length];
1318         if (searchForward) {
1319             [selectionForInitialSearch extendSelectionAtStart:1];
1320             [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
1321         } else {
1322             [selectionForInitialSearch extendSelectionAtEnd:1];
1323             [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
1324         }
1325     }
1326
1327     PDFSelection *foundSelection = [document findString:target fromSelection:selectionForInitialSearch withOptions:options];
1328     [selectionForInitialSearch release];
1329
1330     // If we first searched in the selection, and we found the selection, search again from just past the selection.
1331     if (startInSelection && [foundSelection isEqual:initialSelection])
1332         foundSelection = [document findString:target fromSelection:initialSelection withOptions:options];
1333
1334     if (!foundSelection && wrapSearch)
1335         foundSelection = [document findString:target fromSelection:nil withOptions:options];
1336
1337     return foundSelection;
1338 }
1339
1340 bool PDFPlugin::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1341 {
1342     BOOL searchForward = !(options & FindOptionsBackwards);
1343     BOOL caseSensitive = !(options & FindOptionsCaseInsensitive);
1344     BOOL wrapSearch = options & FindOptionsWrapAround;
1345
1346     unsigned matchCount;
1347     if (!maxMatchCount) {
1348         // If the max was zero, any result means we exceeded the max. We can skip computing the actual count.
1349         matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1350     } else {
1351         matchCount = countFindMatches(target, options, maxMatchCount);
1352         if (matchCount > maxMatchCount)
1353             matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1354     }
1355
1356     if (target.isEmpty()) {
1357         PDFSelection* searchSelection = [m_pdfLayerController searchSelection];
1358         [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1359         [m_pdfLayerController setSearchSelection:searchSelection];
1360         m_lastFoundString = emptyString();
1361         return false;
1362     }
1363
1364     if (m_lastFoundString == target) {
1365         PDFSelection *selection = nextMatchForString(target, searchForward, caseSensitive, wrapSearch, [m_pdfLayerController searchSelection], NO);
1366         if (!selection)
1367             return false;
1368
1369         [m_pdfLayerController setSearchSelection:selection];
1370         [m_pdfLayerController gotoSelection:selection];
1371     } else {
1372         [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1373         m_lastFoundString = target;
1374     }
1375
1376     return matchCount > 0;
1377 }
1378
1379 bool PDFPlugin::performDictionaryLookupAtLocation(const FloatPoint& point)
1380 {
1381     IntPoint localPoint = convertFromRootViewToPlugin(roundedIntPoint(point));
1382     PDFSelection* lookupSelection = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(localPoint)];
1383
1384     if ([[lookupSelection string] length])
1385         [m_pdfLayerController searchInDictionaryWithSelection:lookupSelection];
1386
1387     return true;
1388 }
1389
1390 void PDFPlugin::focusNextAnnotation()
1391 {
1392     [m_pdfLayerController activateNextAnnotation:false];
1393 }
1394
1395 void PDFPlugin::focusPreviousAnnotation()
1396 {
1397     [m_pdfLayerController activateNextAnnotation:true];
1398 }
1399
1400 void PDFPlugin::notifySelectionChanged(PDFSelection *selection)
1401 {
1402     webFrame()->page()->didChangeSelection();
1403 }
1404
1405 String PDFPlugin::getSelectionString() const
1406 {
1407     return [[m_pdfLayerController currentSelection] string];
1408 }
1409
1410 String PDFPlugin::getSelectionForWordAtPoint(const FloatPoint& point) const
1411 {
1412     IntPoint pointInView = convertFromPluginToPDFView(convertFromRootViewToPlugin(roundedIntPoint(point)));
1413     PDFSelection *selectionForWord = [m_pdfLayerController getSelectionForWordAtPoint:pointInView];
1414     [m_pdfLayerController setCurrentSelection:selectionForWord];
1415     
1416     return [selectionForWord string];
1417 }
1418
1419 bool PDFPlugin::existingSelectionContainsPoint(const FloatPoint& locationInViewCoordinates) const
1420 {
1421     PDFSelection *currentSelection = [m_pdfLayerController currentSelection];
1422     if (!currentSelection)
1423         return false;
1424     
1425     IntPoint pointInPDFView = convertFromPluginToPDFView(convertFromRootViewToPlugin(roundedIntPoint(locationInViewCoordinates)));
1426     PDFSelection *selectionForWord = [m_pdfLayerController getSelectionForWordAtPoint:pointInPDFView];
1427
1428     NSUInteger currentPageIndex = [m_pdfLayerController currentPageIndex];
1429     
1430     NSArray *selectionRects = [m_pdfLayerController rectsForSelectionInLayoutSpace:currentSelection];
1431     if (!selectionRects || !selectionRects.count)
1432         return false;
1433     
1434     if (currentPageIndex >= selectionRects.count)
1435         currentPageIndex = selectionRects.count - 1;
1436
1437     NSArray *wordSelectionRects = [m_pdfLayerController rectsForSelectionInLayoutSpace:selectionForWord];
1438     if (!wordSelectionRects || !wordSelectionRects.count)
1439         return false;
1440
1441     NSValue *selectionBounds = [selectionRects objectAtIndex:currentPageIndex];
1442     NSValue *wordSelectionBounds = [wordSelectionRects objectAtIndex:0];
1443
1444     NSRect selectionBoundsRect = selectionBounds.rectValue;
1445     NSRect wordSelectionBoundsRect = wordSelectionBounds.rectValue;
1446     return NSIntersectsRect(wordSelectionBoundsRect, selectionBoundsRect);
1447 }
1448
1449 static NSPoint pointInLayoutSpaceForPointInWindowSpace(PDFLayerController* pdfLayerController, NSPoint pointInView)
1450 {
1451     CGPoint point = NSPointToCGPoint(pointInView);
1452
1453     CGPoint scrollOffset = CGPointZero;
1454     CGFloat scaleFactor = [pdfLayerController contentScaleFactor];
1455
1456     scrollOffset.y = [pdfLayerController contentSizeRespectingZoom].height - NSRectToCGRect([pdfLayerController frame]).size.height - scrollOffset.y;
1457     
1458     CGPoint newPoint = CGPointMake(scrollOffset.x + point.x, scrollOffset.y + point.y);
1459     newPoint.x /= scaleFactor;
1460     newPoint.y /= scaleFactor;
1461     return NSPointFromCGPoint(newPoint);
1462 }
1463
1464 String PDFPlugin::lookupTextAtLocation(const FloatPoint& locationInViewCoordinates, WebHitTestResultData& data, PDFSelection **selectionPtr, NSDictionary **options) const
1465 {
1466     PDFSelection*& selection = *selectionPtr;
1467
1468     selection = [m_pdfLayerController currentSelection];
1469     if (existingSelectionContainsPoint(locationInViewCoordinates))
1470         return selection.string;
1471         
1472     IntPoint pointInPDFView = convertFromPluginToPDFView(convertFromRootViewToPlugin(roundedIntPoint(locationInViewCoordinates)));
1473     selection = [m_pdfLayerController getSelectionForWordAtPoint:pointInPDFView];
1474     if (!selection)
1475         return "";
1476
1477     NSPoint pointInLayoutSpace = pointInLayoutSpaceForPointInWindowSpace(m_pdfLayerController.get(), pointInPDFView);
1478
1479     PDFPage *currentPage = [[m_pdfLayerController layout] pageNearestPoint:pointInLayoutSpace currentPage:[m_pdfLayerController currentPage]];
1480     
1481     NSPoint pointInPageSpace = [[m_pdfLayerController layout] convertPoint:pointInLayoutSpace toPage:currentPage forScaleFactor:1.0];
1482     
1483     for (PDFAnnotation *annotation in currentPage.annotations) {
1484         if (![annotation isKindOfClass:pdfAnnotationLinkClass()])
1485             continue;
1486     
1487         NSRect bounds = annotation.bounds;
1488         if (!NSPointInRect(pointInPageSpace, bounds))
1489             continue;
1490         
1491         PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
1492         NSURL *url = linkAnnotation.URL;
1493         if (!url)
1494             continue;
1495         
1496         data.absoluteLinkURL = url.absoluteString;
1497         data.linkLabel = selection.string;
1498         return selection.string;
1499     }
1500     
1501     NSString *lookupText = DictionaryLookup::stringForPDFSelection(selection, options);
1502     if (!lookupText || !lookupText.length)
1503         return "";
1504
1505     [m_pdfLayerController setCurrentSelection:selection];
1506     return lookupText;
1507 }
1508
1509 static NSRect rectInViewSpaceForRectInLayoutSpace(PDFLayerController* pdfLayerController, NSRect layoutSpaceRect)
1510 {
1511     CGRect newRect = NSRectToCGRect(layoutSpaceRect);
1512     CGFloat scaleFactor = pdfLayerController.contentScaleFactor;
1513     CGPoint scrollOffset = CGPointZero;
1514
1515     scrollOffset.y = pdfLayerController.contentSizeRespectingZoom.height - NSRectToCGRect(pdfLayerController.frame).size.height - scrollOffset.y;
1516
1517     newRect.origin.x *= scaleFactor;
1518     newRect.origin.y *= scaleFactor;
1519     newRect.size.width *= scaleFactor;
1520     newRect.size.height *= scaleFactor;
1521
1522     newRect.origin.x -= scrollOffset.x;
1523     newRect.origin.y -= scrollOffset.y;
1524
1525     return NSRectFromCGRect(newRect);
1526 }
1527
1528 FloatRect PDFPlugin::rectForSelectionInRootView(PDFSelection *selection) const
1529 {
1530     PDFPage *currentPage = nil;
1531     NSArray* pages = selection.pages;
1532     if (pages.count)
1533         currentPage = (PDFPage *)[pages objectAtIndex:0];
1534
1535     if (!currentPage)
1536         currentPage = [m_pdfLayerController currentPage];
1537     
1538     NSRect rectInPageSpace = [selection boundsForPage:currentPage];
1539     NSRect rectInLayoutSpace = [[m_pdfLayerController layout] convertRect:rectInPageSpace fromPage:currentPage forScaleFactor:1.0];
1540     NSRect rectInView = rectInViewSpaceForRectInLayoutSpace(m_pdfLayerController.get(), rectInLayoutSpace);
1541
1542     rectInView.origin = convertFromPDFViewToRootView(IntPoint(rectInView.origin));
1543
1544     return FloatRect(rectInView);
1545 }
1546
1547 void PDFPlugin::performWebSearch(NSString *string)
1548 {
1549     webFrame()->page()->send(Messages::WebPageProxy::SearchTheWeb(string));
1550 }
1551
1552 void PDFPlugin::performSpotlightSearch(NSString *string)
1553 {
1554     webFrame()->page()->send(Messages::WebPageProxy::SearchWithSpotlight(string));
1555 }
1556
1557 NSData *PDFPlugin::liveData() const
1558 {
1559     if (m_activeAnnotation)
1560         m_activeAnnotation->commit();
1561
1562     // Save data straight from the resource instead of PDFKit if the document is
1563     // untouched by the user, so that PDFs which PDFKit can't display will still be downloadable.
1564     if (m_pdfDocumentWasMutated)
1565         return [m_pdfDocument dataRepresentation];
1566     
1567     return rawData();
1568 }
1569
1570 NSObject *PDFPlugin::accessibilityObject() const
1571 {
1572     return m_accessibilityObject.get();
1573 }
1574
1575 void PDFPlugin::scrollToPoint(IntPoint scrollPoint)
1576 {
1577     Frame* frame = pluginView()->frame();
1578     float scale = frame->page()->pageScaleFactor();
1579     scrollPoint.scale(scale);
1580     frame->view()->scrollToOffsetWithoutAnimation(scrollPoint);
1581 }
1582
1583 float PDFPlugin::scaleFactor() const
1584 {
1585     Frame* frame = pluginView()->frame();
1586     return frame->page()->pageScaleFactor();
1587 }
1588
1589 void PDFPlugin::zoomIn()
1590 {
1591     if (webFrame()->isMainFrame() && isFullFramePlugin()) {
1592         WebPage* page = webFrame()->page();
1593         page->scalePage(page->pageScaleFactor() * zoomButtonScaleMultiplier, IntPoint());
1594     } else {
1595         [m_pdfLayerController setContentScaleFactor:[m_pdfLayerController contentScaleFactor] * zoomButtonScaleMultiplier];
1596         calculateSizes();
1597     }
1598 }
1599
1600 void PDFPlugin::zoomOut()
1601 {
1602     if (webFrame()->isMainFrame() && isFullFramePlugin()) {
1603         WebPage* page = webFrame()->page();
1604         page->scalePage(page->pageScaleFactor() / zoomButtonScaleMultiplier, IntPoint());
1605     } else {
1606         [m_pdfLayerController setContentScaleFactor:[m_pdfLayerController contentScaleFactor] / zoomButtonScaleMultiplier];
1607         calculateSizes();
1608     }
1609 }
1610
1611 PDFPlugin::HUD::HUD(PDFPlugin& plugin)
1612     : m_overlay(PageOverlay::create(*this))
1613     , m_plugin(plugin)
1614 {
1615     WebFrame* webFrame = plugin.webFrame();
1616
1617     // In order to avoid the overlay lagging behind main-frame scrolling, we need
1618     // to force synchronous scrolling for the whole page if we have a HUD in a subframe PDF.
1619     m_overlay->setNeedsSynchronousScrolling(!webFrame->isMainFrame());
1620
1621     MainFrame& mainFrame = webFrame->coreFrame()->mainFrame();
1622     mainFrame.pageOverlayController().installPageOverlay(m_overlay.ptr(), PageOverlay::FadeMode::DoNotFade);
1623     m_overlay->setNeedsDisplay();
1624
1625     RefPtr<PlatformCALayer> platformCALayer = downcast<GraphicsLayerCA>(m_overlay->layer()).platformCALayer();
1626     platformCALayer->setOpacity(0);
1627 }
1628
1629 PDFPlugin::HUD::~HUD()
1630 {
1631     MainFrame& mainFrame = m_plugin.webFrame()->coreFrame()->mainFrame();
1632     mainFrame.pageOverlayController().uninstallPageOverlay(m_overlay.ptr(), PageOverlay::FadeMode::DoNotFade);
1633 }
1634
1635 void PDFPlugin::HUD::willMoveToPage(PageOverlay&, Page* page)
1636 {
1637 }
1638
1639 void PDFPlugin::HUD::didMoveToPage(PageOverlay&, Page*)
1640 {
1641 }
1642
1643 IntRect PDFPlugin::HUD::frameInRootView() const
1644 {
1645     // FIXME: These should come from PDFKit.
1646     const int HUDWidth = 236;
1647     const int HUDHeight = 72;
1648     const int HUDYOffset = 50;
1649
1650     FrameView* frameView = m_plugin.webFrame()->coreFrame()->view();
1651     IntRect frameRectInRootView = frameView->convertToRootView(frameView->boundsRect());
1652     FloatRect HUDRect(frameRectInRootView.x() + (frameRectInRootView.width() / 2 - (HUDWidth / 2)), frameRectInRootView.y() + (frameRectInRootView.height() - HUDYOffset - HUDHeight), HUDWidth, HUDHeight);
1653     return enclosingIntRect(HUDRect);
1654 }
1655
1656 bool PDFPlugin::HUD::containsPointInRootView(IntPoint point)
1657 {
1658     if (m_plugin.isLocked())
1659         return false;
1660
1661     return frameInRootView().contains(point);
1662 }
1663
1664 void PDFPlugin::HUD::drawRect(PageOverlay&, GraphicsContext& context, const IntRect& dirtyRect)
1665 {
1666     IntRect frameInRootView = this->frameInRootView();
1667
1668     context.translate(IntSize(frameInRootView.x(), frameInRootView.y() + frameInRootView.height()));
1669     context.scale(FloatSize(1, -1));
1670
1671     IntRect localRect(IntPoint(), frameInRootView.size());
1672     context.clip(localRect);
1673
1674     [m_plugin.pdfLayerController() drawHUDInContext:context.platformContext()];
1675 }
1676
1677 void PDFPlugin::HUD::invalidate()
1678 {
1679     m_overlay->setNeedsDisplay();
1680 }
1681
1682 void PDFPlugin::HUD::setVisible(bool visible, AnimateVisibilityTransition animateTransition)
1683 {
1684     if (visible == m_visible)
1685         return;
1686
1687     m_visible = visible;
1688
1689     float toValue = m_visible ? 0.75 : 0;
1690     RefPtr<PlatformCALayer> platformCALayer = downcast<GraphicsLayerCA>(m_overlay->layer()).platformCALayer();
1691
1692     if (animateTransition == AnimateVisibilityTransition::No) {
1693         platformCALayer->removeAnimationForKey("HUDFade");
1694         platformCALayer->setOpacity(toValue);
1695         return;
1696     }
1697
1698     float duration = m_visible ? 0.25 : 0.5;
1699
1700     RetainPtr<CABasicAnimation> fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
1701     [fadeAnimation setDuration:duration];
1702     [fadeAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
1703     [fadeAnimation setDelegate:[[[WKPDFHUDAnimationDelegate alloc] initWithAnimationCompletionHandler:[platformCALayer, toValue] (bool completed) {
1704         if (completed)
1705             platformCALayer->setOpacity(toValue);
1706
1707     }] autorelease]];
1708     [fadeAnimation setFromValue:@(((CALayer *)platformCALayer->platformLayer().presentationLayer).opacity)];
1709     [fadeAnimation setToValue:@(toValue)];
1710
1711     RefPtr<PlatformCAAnimation> animation = PlatformCAAnimationCocoa::create(fadeAnimation.get());
1712     platformCALayer->removeAnimationForKey("HUDFade");
1713     platformCALayer->addAnimationForKey("HUDFade", *animation);
1714 }
1715
1716 bool PDFPlugin::HUD::mouseEvent(PageOverlay&, const PlatformMouseEvent& event)
1717 {
1718     if (m_plugin.isLocked())
1719         return false;
1720
1721     // We don't want the HUD to appear when dragging past it, or disappear when dragging from a button,
1722     // so don't update visibility while any mouse buttons are pressed.
1723     if (event.button() == MouseButton::NoButton)
1724         setVisible(containsPointInRootView(event.position()), AnimateVisibilityTransition::Yes);
1725
1726     if (!m_visible)
1727         return false;
1728
1729     if (event.button() != MouseButton::LeftButton)
1730         return false;
1731
1732     IntPoint positionInRootViewCoordinates(event.position());
1733     IntRect HUDRectInRootViewCoordinates = frameInRootView();
1734
1735     auto eventWithType = [&](NSEventType eventType) -> NSEvent * {
1736         return [NSEvent mouseEventWithType:eventType location:positionInRootViewCoordinates modifierFlags:0 timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0];
1737     };
1738
1739     auto pdfLayerController = m_plugin.pdfLayerController();
1740
1741     switch (event.type()) {
1742     case PlatformEvent::MouseMoved:
1743         return [pdfLayerController mouseDragged:eventWithType(NSEventTypeLeftMouseDragged) inHUDWithBounds:HUDRectInRootViewCoordinates];
1744     case PlatformEvent::MousePressed:
1745         return [pdfLayerController mouseDown:eventWithType(NSEventTypeLeftMouseDown) inHUDWithBounds:HUDRectInRootViewCoordinates];
1746     case PlatformEvent::MouseReleased:
1747         return [pdfLayerController mouseUp:eventWithType(NSEventTypeLeftMouseUp) inHUDWithBounds:HUDRectInRootViewCoordinates];
1748     default:
1749         break;
1750     }
1751
1752     return false;
1753 }
1754
1755 } // namespace WebKit
1756
1757 #endif // ENABLE(PDFKIT_PLUGIN)