Scrollbars and ScrollAnimators must always have a ScrollableArea
[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 0;
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(int, 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     // As size of the content area changes, scrollbars may need to appear or to disappear.
801     updateScrollbars();
802     
803     ScrollableArea::contentsResized();
804 }
805
806 void PDFPlugin::addArchiveResource()
807 {
808     // FIXME: It's a hack to force add a resource to DocumentLoader. PDF documents should just be fetched as CachedResources.
809
810     // Add just enough data for context menu handling and web archives to work.
811     NSDictionary* headers = @{ @"Content-Disposition": (NSString *)m_suggestedFilename, @"Content-Type" : @"application/pdf" };
812     RetainPtr<NSURLResponse> response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:m_sourceURL statusCode:200 HTTPVersion:(NSString*)kCFHTTPVersion1_1 headerFields:headers]);
813     ResourceResponse synthesizedResponse(response.get());
814
815     RefPtr<ArchiveResource> resource = ArchiveResource::create(SharedBuffer::wrapCFData(m_data.get()), m_sourceURL, "application/pdf", String(), String(), synthesizedResponse);
816     pluginView()->frame()->document()->loader()->addArchiveResource(resource.release());
817 }
818
819 static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object)
820 {
821     PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
822     pdfView->ref();
823 }
824
825 static void jsPDFDocFinalize(JSObjectRef object)
826 {
827     PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(object));
828     pdfView->deref();
829 }
830
831 JSValueRef PDFPlugin::jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
832 {
833     PDFPlugin* pdfView = static_cast<PDFPlugin*>(JSObjectGetPrivate(thisObject));
834
835     WebFrame* frame = pdfView->m_frame;
836     if (!frame)
837         return JSValueMakeUndefined(ctx);
838
839     Frame* coreFrame = frame->coreFrame();
840     if (!coreFrame)
841         return JSValueMakeUndefined(ctx);
842
843     Page* page = coreFrame->page();
844     if (!page)
845         return JSValueMakeUndefined(ctx);
846
847     page->chrome().print(coreFrame);
848
849     return JSValueMakeUndefined(ctx);
850 }
851
852 JSObjectRef PDFPlugin::makeJSPDFDoc(JSContextRef ctx)
853 {
854     static JSStaticFunction jsPDFDocStaticFunctions[] = {
855         { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
856         { 0, 0, 0 },
857     };
858
859     static JSClassDefinition jsPDFDocClassDefinition = {
860         0,
861         kJSClassAttributeNone,
862         "Doc",
863         0,
864         0,
865         jsPDFDocStaticFunctions,
866         jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
867     };
868
869     static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition);
870
871     return JSObjectMake(ctx, jsPDFDocClass, this);
872 }
873
874 static RetainPtr<CFMutableDataRef> convertPostScriptDataToPDF(RetainPtr<CFDataRef> postScriptData)
875 {
876     // Convert PostScript to PDF using the Quartz 2D API.
877     // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html
878
879     CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 };
880     RetainPtr<CGPSConverterRef> converter = adoptCF(CGPSConverterCreate(0, &callbacks, 0));
881     RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateWithCFData(postScriptData.get()));
882     RetainPtr<CFMutableDataRef> pdfData = adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0));
883     RetainPtr<CGDataConsumerRef> consumer = adoptCF(CGDataConsumerCreateWithCFData(pdfData.get()));
884     
885     CGPSConverterConvert(converter.get(), provider.get(), consumer.get(), 0);
886     
887     return pdfData;
888 }
889
890 void PDFPlugin::convertPostScriptDataIfNeeded()
891 {
892     if (!m_isPostScript)
893         return;
894
895     m_suggestedFilename = String(m_suggestedFilename + ".pdf");
896     m_data = convertPostScriptDataToPDF(m_data);
897 }
898
899 void PDFPlugin::pdfDocumentDidLoad()
900 {
901     addArchiveResource();
902     
903     RetainPtr<PDFDocument> document = adoptNS([[pdfDocumentClass() alloc] initWithData:rawData()]);
904
905     setPDFDocument(document);
906
907     updatePageAndDeviceScaleFactors();
908
909     [m_pdfLayerController setFrameSize:size()];
910     m_pdfLayerController.get().document = document.get();
911
912     if (handlesPageScaleFactor())
913         pluginView()->setPageScaleFactor([m_pdfLayerController contentScaleFactor], IntPoint());
914
915     notifyScrollPositionChanged(IntPoint([m_pdfLayerController scrollPosition]));
916
917     calculateSizes();
918     updateScrollbars();
919
920     runScriptsInPDFDocument();
921
922     if ([document isLocked])
923         createPasswordEntryForm();
924 }
925
926 void PDFPlugin::streamDidReceiveResponse(uint64_t streamID, const URL&, uint32_t, uint32_t, const String& mimeType, const String&, const String& suggestedFilename)
927 {
928     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
929
930     m_suggestedFilename = suggestedFilename;
931
932     if (equalIgnoringCase(mimeType, postScriptMIMEType))
933         m_isPostScript = true;
934 }
935
936 void PDFPlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
937 {
938     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
939
940     if (!m_data)
941         m_data = adoptCF(CFDataCreateMutable(0, 0));
942
943     CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
944 }
945
946 void PDFPlugin::streamDidFinishLoading(uint64_t streamID)
947 {
948     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
949
950     convertPostScriptDataIfNeeded();
951     pdfDocumentDidLoad();
952 }
953
954 void PDFPlugin::streamDidFail(uint64_t streamID, bool wasCancelled)
955 {
956     ASSERT_UNUSED(streamID, streamID == pdfDocumentRequestID);
957
958     m_data.clear();
959 }
960
961 void PDFPlugin::manualStreamDidReceiveResponse(const URL& responseURL, uint32_t streamLength,  uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& suggestedFilename)
962 {
963     m_suggestedFilename = suggestedFilename;
964
965     if (equalIgnoringCase(mimeType, postScriptMIMEType))
966         m_isPostScript = true;
967 }
968
969 void PDFPlugin::manualStreamDidReceiveData(const char* bytes, int length)
970 {
971     if (!m_data)
972         m_data = adoptCF(CFDataCreateMutable(0, 0));
973
974     CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(bytes), length);
975 }
976
977 void PDFPlugin::manualStreamDidFinishLoading()
978 {
979     convertPostScriptDataIfNeeded();
980     pdfDocumentDidLoad();
981 }
982
983 void PDFPlugin::manualStreamDidFail(bool)
984 {
985     m_data.clear();
986 }
987
988 void PDFPlugin::runScriptsInPDFDocument()
989 {
990     Vector<RetainPtr<CFStringRef>> scripts;
991     getAllScriptsInPDFDocument([m_pdfDocument documentRef], scripts);
992
993     size_t scriptCount = scripts.size();
994     if (!scriptCount)
995         return;
996
997     JSGlobalContextRef ctx = JSGlobalContextCreate(0);
998     JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx);
999
1000     for (size_t i = 0; i < scriptCount; ++i) {
1001         JSStringRef script = JSStringCreateWithCFString(scripts[i].get());
1002         JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0);
1003         JSStringRelease(script);
1004     }
1005     
1006     JSGlobalContextRelease(ctx);
1007 }
1008
1009 void PDFPlugin::createPasswordEntryForm()
1010 {
1011     m_passwordField = PDFPluginPasswordField::create(m_pdfLayerController.get(), this);
1012     m_passwordField->attach(m_annotationContainer.get());
1013 }
1014
1015 void PDFPlugin::attemptToUnlockPDF(const String& password)
1016 {
1017     [m_pdfLayerController attemptToUnlockWithPassword:password];
1018
1019     if (![pdfDocument() isLocked]) {
1020         m_passwordField = nullptr;
1021
1022         calculateSizes();
1023         updateScrollbars();
1024     }
1025 }
1026
1027 void PDFPlugin::updatePageAndDeviceScaleFactors()
1028 {
1029     double newScaleFactor = controller()->contentsScaleFactor();
1030     if (!handlesPageScaleFactor())
1031         newScaleFactor *= webFrame()->page()->pageScaleFactor();
1032
1033     [m_pdfLayerController setDeviceScaleFactor:newScaleFactor];
1034 }
1035
1036 void PDFPlugin::contentsScaleFactorChanged(float)
1037 {
1038     updatePageAndDeviceScaleFactors();
1039 }
1040
1041 void PDFPlugin::computePageBoxes()
1042 {
1043     size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument documentRef]);
1044     for (size_t i = 0; i < pageCount; ++i) {
1045         CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument documentRef], i + 1);
1046         ASSERT(pdfPage);
1047
1048         CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
1049         if (CGRectIsEmpty(box))
1050             box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
1051         m_pageBoxes.append(IntRect(box));
1052     }
1053 }
1054
1055 void PDFPlugin::calculateSizes()
1056 {
1057     if ([pdfDocument() isLocked]) {
1058         setPDFDocumentSize(IntSize(0, 0));
1059         return;
1060     }
1061
1062     // FIXME: This should come straight from PDFKit.
1063     computePageBoxes();
1064
1065     setPDFDocumentSize(IntSize([m_pdfLayerController contentSizeRespectingZoom]));
1066 }
1067
1068 bool PDFPlugin::initialize(const Parameters& parameters)
1069 {
1070     m_sourceURL = parameters.url;
1071     if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty())
1072         controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false);
1073
1074     controller()->didInitializePlugin();
1075     return true;
1076 }
1077
1078 void PDFPlugin::destroy()
1079 {
1080     m_pdfLayerController.get().delegate = 0;
1081
1082     if (webFrame()) {
1083         if (FrameView* frameView = webFrame()->coreFrame()->view())
1084             frameView->removeScrollableArea(this);
1085     }
1086
1087     m_activeAnnotation = 0;
1088     m_annotationContainer = 0;
1089
1090     destroyScrollbar(HorizontalScrollbar);
1091     destroyScrollbar(VerticalScrollbar);
1092     
1093     [m_scrollCornerLayer removeFromSuperlayer];
1094     [m_contentLayer removeFromSuperlayer];
1095 }
1096
1097 void PDFPlugin::updateControlTints(GraphicsContext* graphicsContext)
1098 {
1099     ASSERT(graphicsContext->updatingControlTints());
1100
1101     if (m_horizontalScrollbar)
1102         m_horizontalScrollbar->invalidate();
1103     if (m_verticalScrollbar)
1104         m_verticalScrollbar->invalidate();
1105     invalidateScrollCorner(scrollCornerRect());
1106 }
1107
1108 void PDFPlugin::paintControlForLayerInContext(CALayer *layer, CGContextRef context)
1109 {
1110     GraphicsContext graphicsContext(context);
1111     GraphicsContextStateSaver stateSaver(graphicsContext);
1112     
1113     graphicsContext.setIsCALayerContext(true);
1114     
1115     if (layer == m_scrollCornerLayer) {
1116         IntRect scrollCornerRect = this->scrollCornerRect();
1117         graphicsContext.translate(-scrollCornerRect.x(), -scrollCornerRect.y());
1118         ScrollbarTheme::theme()->paintScrollCorner(0, &graphicsContext, scrollCornerRect);
1119         return;
1120     }
1121     
1122     Scrollbar* scrollbar = nullptr;
1123     
1124     if (layer == m_verticalScrollbarLayer)
1125         scrollbar = verticalScrollbar();
1126     else if (layer == m_horizontalScrollbarLayer)
1127         scrollbar = horizontalScrollbar();
1128
1129     if (!scrollbar)
1130         return;
1131     
1132     graphicsContext.translate(-scrollbar->x(), -scrollbar->y());
1133     scrollbar->paint(&graphicsContext, scrollbar->frameRect());
1134 }
1135
1136 PassRefPtr<ShareableBitmap> PDFPlugin::snapshot()
1137 {
1138     if (size().isEmpty())
1139         return nullptr;
1140
1141     float contentsScaleFactor = controller()->contentsScaleFactor();
1142     IntSize backingStoreSize = size();
1143     backingStoreSize.scale(contentsScaleFactor);
1144
1145     RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha);
1146     auto context = bitmap->createGraphicsContext();
1147
1148     context->scale(FloatSize(contentsScaleFactor, -contentsScaleFactor));
1149     context->translate(-m_scrollOffset.width(), -m_pdfDocumentSize.height() + m_scrollOffset.height());
1150
1151     [m_pdfLayerController snapshotInContext:context->platformContext()];
1152
1153     return bitmap.release();
1154 }
1155
1156 PlatformLayer* PDFPlugin::pluginLayer()
1157 {
1158     return m_containerLayer.get();
1159 }
1160
1161 IntPoint PDFPlugin::convertFromPluginToPDFView(const IntPoint& point) const
1162 {
1163     return IntPoint(point.x(), size().height() - point.y());
1164 }
1165
1166 IntPoint PDFPlugin::convertFromRootViewToPlugin(const IntPoint& point) const
1167 {
1168     return m_rootViewToPluginTransform.mapPoint(point);
1169 }
1170
1171 IntPoint PDFPlugin::convertFromPDFViewToRootView(const IntPoint& point) const
1172 {
1173     IntPoint pointInPluginCoordinates(point.x(), size().height() - point.y());
1174     return m_rootViewToPluginTransform.inverse().mapPoint(pointInPluginCoordinates);
1175 }
1176
1177 FloatRect PDFPlugin::convertFromPDFViewToScreen(const FloatRect& rect) const
1178 {
1179     FrameView* frameView = webFrame()->coreFrame()->view();
1180
1181     if (!frameView)
1182         return FloatRect();
1183
1184     FloatPoint originInPluginCoordinates(rect.x(), size().height() - rect.y() - rect.height());
1185     FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(FloatRect(originInPluginCoordinates, rect.size()));
1186
1187     return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
1188 }
1189
1190 IntRect PDFPlugin::boundsOnScreen() const
1191 {
1192     FrameView* frameView = webFrame()->coreFrame()->view();
1193
1194     if (!frameView)
1195         return IntRect();
1196
1197     FloatRect bounds = FloatRect(FloatPoint(), size());
1198     FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(bounds);
1199     return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
1200 }
1201
1202 void PDFPlugin::geometryDidChange(const IntSize& pluginSize, const IntRect&, const AffineTransform& pluginToRootViewTransform)
1203 {
1204     if (size() == pluginSize && pluginView()->pageScaleFactor() == [m_pdfLayerController contentScaleFactor])
1205         return;
1206
1207     m_size = pluginSize;
1208     m_rootViewToPluginTransform = pluginToRootViewTransform.inverse();
1209     [m_pdfLayerController setFrameSize:pluginSize];
1210
1211     [CATransaction begin];
1212     [CATransaction setDisableActions:YES];
1213     CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
1214     transform = CATransform3DTranslate(transform, 0, -pluginSize.height(), 0);
1215     
1216     if (handlesPageScaleFactor()) {
1217         CGFloat magnification = pluginView()->pageScaleFactor() - [m_pdfLayerController contentScaleFactor];
1218
1219         // FIXME: Instead of m_lastMousePositionInPluginCoordinates, we should use the zoom origin from PluginView::setPageScaleFactor.
1220         if (magnification)
1221             [m_pdfLayerController magnifyWithMagnification:magnification atPoint:convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates) immediately:NO];
1222     } else {
1223         // If we don't handle page scale ourselves, we need to respect our parent page's
1224         // scale, which may have changed.
1225         updatePageAndDeviceScaleFactors();
1226     } 
1227
1228     calculateSizes();
1229     updateScrollbars();
1230
1231     if (m_activeAnnotation)
1232         m_activeAnnotation->updateGeometry();
1233
1234     [m_contentLayer setSublayerTransform:transform];
1235     [CATransaction commit];
1236 }
1237
1238 void PDFPlugin::frameDidFinishLoading(uint64_t)
1239 {
1240     ASSERT_NOT_REACHED();
1241 }
1242
1243 void PDFPlugin::frameDidFail(uint64_t, bool)
1244 {
1245     ASSERT_NOT_REACHED();
1246 }
1247
1248 void PDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&)
1249 {
1250     ASSERT_NOT_REACHED();
1251 }
1252
1253     
1254 static NSUInteger modifierFlagsFromWebEvent(const WebEvent& event)
1255 {
1256     return (event.shiftKey() ? NSShiftKeyMask : 0)
1257         | (event.controlKey() ? NSControlKeyMask : 0)
1258         | (event.altKey() ? NSAlternateKeyMask : 0)
1259         | (event.metaKey() ? NSCommandKeyMask : 0);
1260 }
1261     
1262 static bool getEventTypeFromWebEvent(const WebEvent& event, NSEventType& eventType)
1263 {
1264     switch (event.type()) {
1265     case WebEvent::KeyDown:
1266         eventType = NSKeyDown;
1267         return true;
1268     case WebEvent::KeyUp:
1269         eventType = NSKeyUp;
1270         return true;
1271     case WebEvent::MouseDown:
1272         switch (static_cast<const WebMouseEvent&>(event).button()) {
1273         case WebMouseEvent::LeftButton:
1274             eventType = NSLeftMouseDown;
1275             return true;
1276         case WebMouseEvent::RightButton:
1277             eventType = NSRightMouseDown;
1278             return true;
1279         default:
1280             return false;
1281         }
1282     case WebEvent::MouseUp:
1283         switch (static_cast<const WebMouseEvent&>(event).button()) {
1284         case WebMouseEvent::LeftButton:
1285             eventType = NSLeftMouseUp;
1286             return true;
1287         case WebMouseEvent::RightButton:
1288             eventType = NSRightMouseUp;
1289             return true;
1290         default:
1291             return false;
1292         }
1293     case WebEvent::MouseMove:
1294         switch (static_cast<const WebMouseEvent&>(event).button()) {
1295         case WebMouseEvent::LeftButton:
1296             eventType = NSLeftMouseDragged;
1297             return true;
1298         case WebMouseEvent::RightButton:
1299             eventType = NSRightMouseDragged;
1300             return true;
1301         case WebMouseEvent::NoButton:
1302             eventType = NSMouseMoved;
1303             return true;
1304         default:
1305             return false;
1306         }
1307     default:
1308         return false;
1309     }
1310 }
1311     
1312 NSEvent *PDFPlugin::nsEventForWebMouseEvent(const WebMouseEvent& event)
1313 {
1314     m_lastMousePositionInPluginCoordinates = convertFromRootViewToPlugin(event.position());
1315
1316     IntPoint positionInPDFViewCoordinates(convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates));
1317
1318     NSEventType eventType;
1319
1320     if (!getEventTypeFromWebEvent(event, eventType))
1321         return 0;
1322
1323     NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1324
1325     return [NSEvent mouseEventWithType:eventType location:positionInPDFViewCoordinates modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0];
1326 }
1327
1328 void PDFPlugin::updateCursor(const WebMouseEvent& event, UpdateCursorMode mode)
1329 {
1330     HitTestResult hitTestResult = None;
1331
1332     PDFSelection *selectionUnderMouse = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(event.position())];
1333     if (selectionUnderMouse && [[selectionUnderMouse string] length])
1334         hitTestResult = Text;
1335
1336     if (hitTestResult == m_lastHitTestResult && mode == UpdateIfNeeded)
1337         return;
1338
1339     webFrame()->page()->send(Messages::WebPageProxy::SetCursor(hitTestResult == Text ? iBeamCursor() : pointerCursor()));
1340     m_lastHitTestResult = hitTestResult;
1341 }
1342
1343 bool PDFPlugin::handleMouseEvent(const WebMouseEvent& event)
1344 {
1345     PlatformMouseEvent platformEvent = platform(event);
1346     IntPoint mousePosition = convertFromRootViewToPlugin(event.position());
1347
1348     m_lastMouseEvent = event;
1349
1350     RefPtr<Scrollbar> targetScrollbar;
1351     RefPtr<Scrollbar> targetScrollbarForLastMousePosition;
1352
1353     if (m_verticalScrollbarLayer) {
1354         IntRect verticalScrollbarFrame(m_verticalScrollbarLayer.get().frame);
1355         if (verticalScrollbarFrame.contains(mousePosition))
1356             targetScrollbar = verticalScrollbar();
1357         if (verticalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1358             targetScrollbarForLastMousePosition = verticalScrollbar();
1359     }
1360
1361     if (m_horizontalScrollbarLayer) {
1362         IntRect horizontalScrollbarFrame(m_horizontalScrollbarLayer.get().frame);
1363         if (horizontalScrollbarFrame.contains(mousePosition))
1364             targetScrollbar = horizontalScrollbar();
1365         if (horizontalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1366             targetScrollbarForLastMousePosition = horizontalScrollbar();
1367     }
1368
1369     if (m_scrollCornerLayer && IntRect(m_scrollCornerLayer.get().frame).contains(mousePosition))
1370         return false;
1371
1372     if ([pdfDocument() isLocked])
1373         return false;
1374
1375     // Right-clicks and Control-clicks always call handleContextMenuEvent as well.
1376     if (event.button() == WebMouseEvent::RightButton || (event.button() == WebMouseEvent::LeftButton && event.controlKey()))
1377         return true;
1378
1379     NSEvent *nsEvent = nsEventForWebMouseEvent(event);
1380
1381     switch (event.type()) {
1382     case WebEvent::MouseMove:
1383         mouseMovedInContentArea();
1384         updateCursor(event);
1385
1386         if (targetScrollbar) {
1387             if (!targetScrollbarForLastMousePosition) {
1388                 targetScrollbar->mouseEntered();
1389                 return true;
1390             }
1391             return targetScrollbar->mouseMoved(platformEvent);
1392         }
1393
1394         if (!targetScrollbar && targetScrollbarForLastMousePosition)
1395             targetScrollbarForLastMousePosition->mouseExited();
1396
1397         switch (event.button()) {
1398         case WebMouseEvent::LeftButton:
1399             [m_pdfLayerController mouseDragged:nsEvent];
1400             return true;
1401         case WebMouseEvent::RightButton:
1402         case WebMouseEvent::MiddleButton:
1403             return false;
1404         case WebMouseEvent::NoButton:
1405             [m_pdfLayerController mouseMoved:nsEvent];
1406             return true;
1407         }
1408     case WebEvent::MouseDown:
1409         switch (event.button()) {
1410         case WebMouseEvent::LeftButton:
1411             if (targetScrollbar)
1412                 return targetScrollbar->mouseDown(platformEvent);
1413
1414             [m_pdfLayerController mouseDown:nsEvent];
1415             return true;
1416         case WebMouseEvent::RightButton:
1417             [m_pdfLayerController rightMouseDown:nsEvent];
1418             return true;
1419         case WebMouseEvent::MiddleButton:
1420         case WebMouseEvent::NoButton:
1421             return false;
1422         }
1423     case WebEvent::MouseUp:
1424         switch (event.button()) {
1425         case WebMouseEvent::LeftButton:
1426             if (targetScrollbar)
1427                 return targetScrollbar->mouseUp(platformEvent);
1428
1429             [m_pdfLayerController mouseUp:nsEvent];
1430             return true;
1431         case WebMouseEvent::RightButton:
1432         case WebMouseEvent::MiddleButton:
1433         case WebMouseEvent::NoButton:
1434             return false;
1435         }
1436     default:
1437         break;
1438     }
1439
1440     return false;
1441 }
1442
1443 bool PDFPlugin::handleMouseEnterEvent(const WebMouseEvent& event)
1444 {
1445     mouseEnteredContentArea();
1446     updateCursor(event, ForceUpdate);
1447     return false;
1448 }
1449
1450 bool PDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&)
1451 {
1452     mouseExitedContentArea();
1453     return false;
1454 }
1455     
1456 bool PDFPlugin::showContextMenuAtPoint(const IntPoint& point)
1457 {
1458     FrameView* frameView = webFrame()->coreFrame()->view();
1459     IntPoint contentsPoint = frameView->contentsToRootView(point);
1460     WebMouseEvent event(WebEvent::MouseDown, WebMouseEvent::RightButton, contentsPoint, contentsPoint, 0, 0, 0, 1, static_cast<WebEvent::Modifiers>(0), monotonicallyIncreasingTime());
1461     return handleContextMenuEvent(event);
1462 }
1463
1464 bool PDFPlugin::handleContextMenuEvent(const WebMouseEvent& event)
1465 {
1466     FrameView* frameView = webFrame()->coreFrame()->view();
1467     IntPoint point = frameView->contentsToScreen(IntRect(frameView->windowToContents(event.position()), IntSize())).location();
1468     
1469     if (NSMenu *nsMenu = [m_pdfLayerController menuForEvent:nsEventForWebMouseEvent(event)]) {
1470         WKPopupContextMenu(nsMenu, point);
1471         return true;
1472     }
1473     
1474     return false;
1475 }
1476
1477 bool PDFPlugin::handleKeyboardEvent(const WebKeyboardEvent& event)
1478 {
1479     NSEventType eventType;
1480
1481     if (!getEventTypeFromWebEvent(event, eventType))
1482         return false;
1483
1484     NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1485     
1486     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()];
1487     
1488     switch (event.type()) {
1489     case WebEvent::KeyDown:
1490         return [m_pdfLayerController keyDown:fakeEvent];
1491     default:
1492         return false;
1493     }
1494     
1495     return false;
1496 }
1497     
1498 bool PDFPlugin::handleEditingCommand(const String& commandName, const String& argument)
1499 {
1500     if (commandName == "copy")
1501         [m_pdfLayerController copySelection];
1502     else if (commandName == "selectAll")
1503         [m_pdfLayerController selectAll];
1504     else if (commandName == "takeFindStringFromSelection") {
1505         NSString *string = [m_pdfLayerController currentSelection].string;
1506         if (string.length)
1507             writeItemsToPasteboard(NSFindPboard, @[ [string dataUsingEncoding:NSUTF8StringEncoding] ], @[ NSPasteboardTypeString ]);
1508     }
1509
1510     return true;
1511 }
1512
1513 bool PDFPlugin::isEditingCommandEnabled(const String& commandName)
1514 {
1515     if (commandName == "copy" || commandName == "takeFindStringFromSelection")
1516         return [m_pdfLayerController currentSelection];
1517
1518     if (commandName == "selectAll")
1519         return true;
1520
1521     return false;
1522 }
1523
1524 void PDFPlugin::setScrollOffset(const IntPoint& offset)
1525 {
1526     m_scrollOffset = IntSize(offset.x(), offset.y());
1527
1528     [CATransaction begin];
1529     [m_pdfLayerController setScrollPosition:offset];
1530
1531     if (m_activeAnnotation)
1532         m_activeAnnotation->updateGeometry();
1533
1534     [CATransaction commit];
1535 }
1536
1537 void PDFPlugin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
1538 {
1539     if (scrollbar == horizontalScrollbar())
1540         [m_horizontalScrollbarLayer setNeedsDisplay];
1541     else if (scrollbar == verticalScrollbar())
1542         [m_verticalScrollbarLayer setNeedsDisplay];
1543 }
1544
1545 void PDFPlugin::invalidateScrollCornerRect(const IntRect& rect)
1546 {
1547     [m_scrollCornerLayer setNeedsDisplay];
1548 }
1549
1550 bool PDFPlugin::isFullFramePlugin()
1551 {
1552     // <object> or <embed> plugins will appear to be in their parent frame, so we have to
1553     // check whether our frame's widget is exactly our PluginView.
1554     Document* document = webFrame()->coreFrame()->document();
1555     return document->isPluginDocument() && static_cast<PluginDocument*>(document)->pluginWidget() == pluginView();
1556 }
1557
1558 bool PDFPlugin::handlesPageScaleFactor()
1559 {
1560     return webFrame()->isMainFrame() && isFullFramePlugin();
1561 }
1562
1563 void PDFPlugin::clickedLink(NSURL *url)
1564 {
1565     Frame* frame = webFrame()->coreFrame();
1566
1567     RefPtr<Event> coreEvent;
1568     if (m_lastMouseEvent.type() != WebEvent::NoType)
1569         coreEvent = MouseEvent::create(eventNames().clickEvent, frame->document()->defaultView(), platform(m_lastMouseEvent), 0, 0);
1570
1571     frame->loader().urlSelected(url, emptyString(), coreEvent.get(), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer);
1572 }
1573
1574 void PDFPlugin::setActiveAnnotation(PDFAnnotation *annotation)
1575 {
1576     if (!supportsForms())
1577         return;
1578
1579     if (m_activeAnnotation)
1580         m_activeAnnotation->commit();
1581
1582     if (annotation) {
1583         if ([annotation isKindOfClass:pdfAnnotationTextWidgetClass()] && static_cast<PDFAnnotationTextWidget *>(annotation).isReadOnly) {
1584             m_activeAnnotation = 0;
1585             return;
1586         }
1587
1588         m_activeAnnotation = PDFPluginAnnotation::create(annotation, m_pdfLayerController.get(), this);
1589         m_activeAnnotation->attach(m_annotationContainer.get());
1590     } else
1591         m_activeAnnotation = 0;
1592 }
1593
1594 bool PDFPlugin::supportsForms()
1595 {
1596     // 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.
1597     return isFullFramePlugin();
1598 }
1599
1600 void PDFPlugin::notifyContentScaleFactorChanged(CGFloat scaleFactor)
1601 {
1602     if (handlesPageScaleFactor())
1603         pluginView()->setPageScaleFactor(scaleFactor, IntPoint());
1604
1605     calculateSizes();
1606     updateScrollbars();
1607 }
1608
1609 void PDFPlugin::notifyDisplayModeChanged(int)
1610 {
1611     calculateSizes();
1612     updateScrollbars();
1613 }
1614
1615 PassRefPtr<SharedBuffer> PDFPlugin::liveResourceData() const
1616 {
1617     NSData *pdfData = liveData();
1618
1619     if (!pdfData)
1620         return 0;
1621
1622     return SharedBuffer::wrapNSData(pdfData);
1623 }
1624
1625 void PDFPlugin::saveToPDF()
1626 {
1627     // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1628     // PDFViewController does an NSBeep(), but that seems insufficient.
1629     if (!pdfDocument())
1630         return;
1631
1632     NSData *data = liveData();
1633     webFrame()->page()->savePDFToFileInDownloadsFolder(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length]);
1634 }
1635
1636 void PDFPlugin::openWithNativeApplication()
1637 {
1638     if (!m_temporaryPDFUUID) {
1639         // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1640         // PDFViewController does an NSBeep(), but that seems insufficient.
1641         if (!pdfDocument())
1642             return;
1643
1644         NSData *data = liveData();
1645
1646         m_temporaryPDFUUID = WebCore::createCanonicalUUIDString();
1647         ASSERT(m_temporaryPDFUUID);
1648
1649         webFrame()->page()->savePDFToTemporaryFolderAndOpenWithNativeApplication(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length], m_temporaryPDFUUID);
1650         return;
1651     }
1652
1653     webFrame()->page()->send(Messages::WebPageProxy::OpenPDFFromTemporaryFolderWithNativeApplication(m_temporaryPDFUUID));
1654 }
1655
1656 void PDFPlugin::writeItemsToPasteboard(NSString *pasteboardName, NSArray *items, NSArray *types)
1657 {
1658     Vector<String> pasteboardTypes;
1659
1660     for (NSString *type in types)
1661         pasteboardTypes.append(type);
1662
1663     uint64_t newChangeCount;
1664     auto& webProcess = WebProcess::singleton();
1665     webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardTypes(pasteboardName, pasteboardTypes),
1666         Messages::WebPasteboardProxy::SetPasteboardTypes::Reply(newChangeCount), 0);
1667
1668     for (NSUInteger i = 0, count = items.count; i < count; ++i) {
1669         NSString *type = [types objectAtIndex:i];
1670         NSData *data = [items objectAtIndex:i];
1671
1672         // We don't expect the data for any items to be empty, but aren't completely sure.
1673         // Avoid crashing in the SharedMemory constructor in release builds if we're wrong.
1674         ASSERT(data.length);
1675         if (!data.length)
1676             continue;
1677
1678         if ([type isEqualToString:NSStringPboardType] || [type isEqualToString:NSPasteboardTypeString]) {
1679             RetainPtr<NSString> plainTextString = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
1680             webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardStringForType(pasteboardName, type, plainTextString.get()), Messages::WebPasteboardProxy::SetPasteboardStringForType::Reply(newChangeCount), 0);
1681         } else {
1682             RefPtr<SharedBuffer> buffer = SharedBuffer::wrapNSData(data);
1683
1684             if (!buffer)
1685                 continue;
1686
1687             SharedMemory::Handle handle;
1688             RefPtr<SharedMemory> sharedMemory = SharedMemory::create(buffer->size());
1689             memcpy(sharedMemory->data(), buffer->data(), buffer->size());
1690             sharedMemory->createHandle(handle, SharedMemory::ReadOnly);
1691             webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardBufferForType(pasteboardName, type, handle, buffer->size()), Messages::WebPasteboardProxy::SetPasteboardBufferForType::Reply(newChangeCount), 0);
1692         }
1693     }
1694 }
1695
1696 void PDFPlugin::showDefinitionForAttributedString(NSAttributedString *string, CGPoint point)
1697 {
1698     DictionaryPopupInfo dictionaryPopupInfo;
1699     dictionaryPopupInfo.origin = convertFromPDFViewToRootView(IntPoint(point));
1700     dictionaryPopupInfo.attributedString.string = string;
1701
1702     webFrame()->page()->send(Messages::WebPageProxy::DidPerformDictionaryLookup(dictionaryPopupInfo));
1703 }
1704
1705 unsigned PDFPlugin::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1706 {
1707     if (!target.length())
1708         return 0;
1709
1710     int nsOptions = (options & FindOptionsCaseInsensitive) ? NSCaseInsensitiveSearch : 0;
1711
1712     return [[pdfDocument() findString:target withOptions:nsOptions] count];
1713 }
1714
1715 PDFSelection *PDFPlugin::nextMatchForString(const String& target, BOOL searchForward, BOOL caseSensitive, BOOL wrapSearch, PDFSelection *initialSelection, BOOL startInSelection)
1716 {
1717     if (!target.length())
1718         return nil;
1719
1720     NSStringCompareOptions options = 0;
1721     if (!searchForward)
1722         options |= NSBackwardsSearch;
1723
1724     if (!caseSensitive)
1725         options |= NSCaseInsensitiveSearch;
1726
1727     PDFDocument *document = pdfDocument().get();
1728
1729     PDFSelection *selectionForInitialSearch = [initialSelection copy];
1730     if (startInSelection) {
1731         // Initially we want to include the selected text in the search. So we must modify the starting search
1732         // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before
1733         // the current selection (if searching forwards) or after (if searching backwards).
1734         int initialSelectionLength = [[initialSelection string] length];
1735         if (searchForward) {
1736             [selectionForInitialSearch extendSelectionAtStart:1];
1737             [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
1738         } else {
1739             [selectionForInitialSearch extendSelectionAtEnd:1];
1740             [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
1741         }
1742     }
1743
1744     PDFSelection *foundSelection = [document findString:target fromSelection:selectionForInitialSearch withOptions:options];
1745     [selectionForInitialSearch release];
1746
1747     // If we first searched in the selection, and we found the selection, search again from just past the selection.
1748     if (startInSelection && [foundSelection isEqual:initialSelection])
1749         foundSelection = [document findString:target fromSelection:initialSelection withOptions:options];
1750
1751     if (!foundSelection && wrapSearch)
1752         foundSelection = [document findString:target fromSelection:nil withOptions:options];
1753
1754     return foundSelection;
1755 }
1756
1757 bool PDFPlugin::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1758 {
1759     BOOL searchForward = !(options & FindOptionsBackwards);
1760     BOOL caseSensitive = !(options & FindOptionsCaseInsensitive);
1761     BOOL wrapSearch = options & FindOptionsWrapAround;
1762
1763     unsigned matchCount;
1764     if (!maxMatchCount) {
1765         // If the max was zero, any result means we exceeded the max. We can skip computing the actual count.
1766         matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1767     } else {
1768         matchCount = countFindMatches(target, options, maxMatchCount);
1769         if (matchCount > maxMatchCount)
1770             matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1771     }
1772
1773     if (target.isEmpty()) {
1774         PDFSelection* searchSelection = [m_pdfLayerController searchSelection];
1775         [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1776         [m_pdfLayerController setSearchSelection:searchSelection];
1777         m_lastFoundString = emptyString();
1778         return false;
1779     }
1780
1781     if (m_lastFoundString == target) {
1782         PDFSelection *selection = nextMatchForString(target, searchForward, caseSensitive, wrapSearch, [m_pdfLayerController searchSelection], NO);
1783         if (!selection)
1784             return false;
1785
1786         [m_pdfLayerController setSearchSelection:selection];
1787         [m_pdfLayerController gotoSelection:selection];
1788     } else {
1789         [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1790         m_lastFoundString = target;
1791     }
1792
1793     return matchCount > 0;
1794 }
1795
1796 bool PDFPlugin::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
1797 {
1798     IntPoint localPoint = convertFromRootViewToPlugin(roundedIntPoint(point));
1799     PDFSelection* lookupSelection = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(localPoint)];
1800
1801     if ([[lookupSelection string] length])
1802         [m_pdfLayerController searchInDictionaryWithSelection:lookupSelection];
1803
1804     return true;
1805 }
1806
1807 void PDFPlugin::focusNextAnnotation()
1808 {
1809     [m_pdfLayerController activateNextAnnotation:false];
1810 }
1811
1812 void PDFPlugin::focusPreviousAnnotation()
1813 {
1814     [m_pdfLayerController activateNextAnnotation:true];
1815 }
1816
1817 void PDFPlugin::notifySelectionChanged(PDFSelection *)
1818 {
1819     webFrame()->page()->didChangeSelection();
1820 }
1821
1822 String PDFPlugin::getSelectionString() const
1823 {
1824     return [[m_pdfLayerController currentSelection] string];
1825 }
1826
1827 void PDFPlugin::performWebSearch(NSString *string)
1828 {
1829     webFrame()->page()->send(Messages::WebPageProxy::SearchTheWeb(string));
1830 }
1831
1832 void PDFPlugin::performSpotlightSearch(NSString *string)
1833 {
1834     webFrame()->page()->send(Messages::WebPageProxy::SearchWithSpotlight(string));
1835 }
1836
1837 bool PDFPlugin::handleWheelEvent(const WebWheelEvent& event)
1838 {
1839     PDFDisplayMode displayMode = [m_pdfLayerController displayMode];
1840
1841     if (displayMode == kPDFDisplaySinglePageContinuous || displayMode == kPDFDisplayTwoUpContinuous)
1842         return ScrollableArea::handleWheelEvent(platform(event));
1843
1844     NSUInteger currentPageIndex = [m_pdfLayerController currentPageIndex];
1845     bool inFirstPage = !currentPageIndex;
1846     bool inLastPage = [m_pdfLayerController lastPageIndex] == currentPageIndex;
1847
1848     bool atScrollTop = !scrollPosition().y();
1849     bool atScrollBottom = scrollPosition().y() == maximumScrollPosition().y();
1850
1851     bool inMomentumScroll = event.momentumPhase() != WebWheelEvent::PhaseNone;
1852
1853     int scrollMagnitudeThresholdForPageFlip = defaultScrollMagnitudeThresholdForPageFlip;
1854
1855     // Imprecise input devices should have a lower threshold so that "clicky" scroll wheels can flip pages.
1856     if (!event.hasPreciseScrollingDeltas())
1857         scrollMagnitudeThresholdForPageFlip = 0;
1858
1859     if (atScrollBottom && !inLastPage && event.delta().height() < 0) {
1860         if (event.delta().height() <= -scrollMagnitudeThresholdForPageFlip && !inMomentumScroll)
1861             [m_pdfLayerController gotoNextPage];
1862         return true;
1863     }
1864
1865     if (atScrollTop && !inFirstPage && event.delta().height() > 0) {
1866         if (event.delta().height() >= scrollMagnitudeThresholdForPageFlip && !inMomentumScroll) {
1867             [CATransaction begin];
1868             [m_pdfLayerController gotoPreviousPage];
1869             scrollToOffsetWithoutAnimation(maximumScrollPosition());
1870             [CATransaction commit];
1871         }
1872         return true;
1873     }
1874
1875     return ScrollableArea::handleWheelEvent(platform(event));
1876 }
1877
1878 NSData *PDFPlugin::liveData() const
1879 {
1880     if (m_activeAnnotation)
1881         m_activeAnnotation->commit();
1882
1883     // Save data straight from the resource instead of PDFKit if the document is
1884     // untouched by the user, so that PDFs which PDFKit can't display will still be downloadable.
1885     if (m_pdfDocumentWasMutated)
1886         return [m_pdfDocument dataRepresentation];
1887     
1888     return rawData();
1889 }
1890
1891 NSObject *PDFPlugin::accessibilityObject() const
1892 {
1893     return m_accessibilityObject.get();
1894 }
1895
1896 } // namespace WebKit
1897
1898 #endif // ENABLE(PDFKIT_PLUGIN)