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