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