javascript: links in inline PDFs shouldn't execute JavaScript in the context of the...
[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     info.clientLoadPolicy = PluginLoadClientPolicyUndefined;
543
544     MimeClassInfo pdfMimeClassInfo;
545     pdfMimeClassInfo.type = "application/pdf";
546     pdfMimeClassInfo.desc = pdfDocumentTypeDescription();
547     pdfMimeClassInfo.extensions.append("pdf");
548     info.mimes.append(pdfMimeClassInfo);
549
550     MimeClassInfo textPDFMimeClassInfo;
551     textPDFMimeClassInfo.type = "text/pdf";
552     textPDFMimeClassInfo.desc = pdfDocumentTypeDescription();
553     textPDFMimeClassInfo.extensions.append("pdf");
554     info.mimes.append(textPDFMimeClassInfo);
555
556     MimeClassInfo postScriptMimeClassInfo;
557     postScriptMimeClassInfo.type = postScriptMIMEType;
558     postScriptMimeClassInfo.desc = postScriptDocumentTypeDescription();
559     postScriptMimeClassInfo.extensions.append("ps");
560     info.mimes.append(postScriptMimeClassInfo);
561     
562     return info;
563 }
564
565 void PDFPlugin::updateScrollbars()
566 {
567     bool hadScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
568
569     if (m_horizontalScrollbar) {
570         if (m_size.width() >= m_pdfDocumentSize.width())
571             destroyScrollbar(HorizontalScrollbar);
572     } else if (m_size.width() < m_pdfDocumentSize.width())
573         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
574
575     if (m_verticalScrollbar) {
576         if (m_size.height() >= m_pdfDocumentSize.height())
577             destroyScrollbar(VerticalScrollbar);
578     } else if (m_size.height() < m_pdfDocumentSize.height())
579         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
580
581     int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
582     int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
583
584     int pageStep = m_pageBoxes.isEmpty() ? 0 : m_pageBoxes[0].height();
585
586     if (m_horizontalScrollbar) {
587         m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
588         m_horizontalScrollbar->setProportion(m_size.width() - verticalScrollbarWidth, m_pdfDocumentSize.width());
589         IntRect scrollbarRect(pluginView()->x(), pluginView()->y() + m_size.height() - m_horizontalScrollbar->height(), m_size.width(), m_horizontalScrollbar->height());
590         if (m_verticalScrollbar)
591             scrollbarRect.contract(m_verticalScrollbar->width(), 0);
592         m_horizontalScrollbar->setFrameRect(scrollbarRect);
593     }
594     if (m_verticalScrollbar) {
595         m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
596         m_verticalScrollbar->setProportion(m_size.height() - horizontalScrollbarHeight, m_pdfDocumentSize.height());
597         IntRect scrollbarRect(IntRect(pluginView()->x() + m_size.width() - m_verticalScrollbar->width(), pluginView()->y(), m_verticalScrollbar->width(), m_size.height()));
598         if (m_horizontalScrollbar)
599             scrollbarRect.contract(0, m_horizontalScrollbar->height());
600         m_verticalScrollbar->setFrameRect(scrollbarRect);
601     }
602
603     FrameView* frameView = m_frame->coreFrame()->view();
604     if (!frameView)
605         return;
606
607     bool hasScrollbars = m_horizontalScrollbar || m_verticalScrollbar;
608     if (hadScrollbars != hasScrollbars) {
609         if (hasScrollbars)
610             frameView->addScrollableArea(this);
611         else
612             frameView->removeScrollableArea(this);
613
614         frameView->setNeedsLayout();
615     }
616     
617     if (m_verticalScrollbarLayer) {
618         m_verticalScrollbarLayer.get().frame = verticalScrollbar()->frameRect();
619         [m_verticalScrollbarLayer setNeedsDisplay];
620     }
621     
622     if (m_horizontalScrollbarLayer) {
623         m_horizontalScrollbarLayer.get().frame = horizontalScrollbar()->frameRect();
624         [m_horizontalScrollbarLayer setNeedsDisplay];
625     }
626     
627     if (m_scrollCornerLayer) {
628         m_scrollCornerLayer.get().frame = scrollCornerRect();
629         [m_scrollCornerLayer setNeedsDisplay];
630     }
631 }
632
633 PluginView* PDFPlugin::pluginView()
634 {
635     return static_cast<PluginView*>(controller());
636 }
637
638 const PluginView* PDFPlugin::pluginView() const
639 {
640     return static_cast<const PluginView*>(controller());
641 }
642
643 PassRefPtr<Scrollbar> PDFPlugin::createScrollbar(ScrollbarOrientation orientation)
644 {
645     RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar);
646     if (orientation == HorizontalScrollbar) {
647         m_horizontalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
648         [m_containerLayer addSublayer:m_horizontalScrollbarLayer.get()];
649     } else {
650         m_verticalScrollbarLayer = adoptNS([[WKPDFPluginScrollbarLayer alloc] initWithPDFPlugin:this]);
651         [m_containerLayer addSublayer:m_verticalScrollbarLayer.get()];
652     }
653     didAddScrollbar(widget.get(), orientation);
654     pluginView()->frame()->view()->addChild(widget.get());
655     return widget.release();
656 }
657
658 void PDFPlugin::destroyScrollbar(ScrollbarOrientation orientation)
659 {
660     RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_horizontalScrollbar : m_verticalScrollbar;
661     if (!scrollbar)
662         return;
663
664     willRemoveScrollbar(scrollbar.get(), orientation);
665     scrollbar->removeFromParent();
666     scrollbar = nullptr;
667
668     if (orientation == HorizontalScrollbar) {
669         [m_horizontalScrollbarLayer removeFromSuperlayer];
670         m_horizontalScrollbarLayer = 0;
671     } else {
672         [m_verticalScrollbarLayer removeFromSuperlayer];
673         m_verticalScrollbarLayer = 0;
674     }
675 }
676
677 IntRect PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
678 {
679     IntRect rect = scrollbarRect;
680     rect.move(scrollbar->location() - pluginView()->location());
681
682     return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->renderer(), rect);
683 }
684
685 IntRect PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
686 {
687     IntRect rect = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->renderer(), parentRect);
688     rect.move(pluginView()->location() - scrollbar->location());
689
690     return rect;
691 }
692
693 IntPoint PDFPlugin::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
694 {
695     IntPoint point = scrollbarPoint;
696     point.move(scrollbar->location() - pluginView()->location());
697
698     return pluginView()->frame()->view()->convertFromRendererToContainingView(pluginView()->renderer(), point);
699 }
700
701 IntPoint PDFPlugin::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
702 {
703     IntPoint point = pluginView()->frame()->view()->convertFromContainingViewToRenderer(pluginView()->renderer(), parentPoint);
704     point.move(pluginView()->location() - scrollbar->location());
705     
706     return point;
707 }
708
709 bool PDFPlugin::handleScroll(ScrollDirection direction, ScrollGranularity granularity)
710 {
711     return scroll(direction, granularity);
712 }
713
714 IntRect PDFPlugin::scrollCornerRect() const
715 {
716     if (!m_horizontalScrollbar || !m_verticalScrollbar)
717         return IntRect();
718     if (m_horizontalScrollbar->isOverlayScrollbar()) {
719         ASSERT(m_verticalScrollbar->isOverlayScrollbar());
720         return IntRect();
721     }
722     return IntRect(pluginView()->width() - m_verticalScrollbar->width(), pluginView()->height() - m_horizontalScrollbar->height(), m_verticalScrollbar->width(), m_horizontalScrollbar->height());
723 }
724
725 ScrollableArea* PDFPlugin::enclosingScrollableArea() const
726 {
727     // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer.
728     return nullptr;
729 }
730
731 IntRect PDFPlugin::scrollableAreaBoundingBox() const
732 {
733     return pluginView()->frameRect();
734 }
735
736 int PDFPlugin::scrollSize(ScrollbarOrientation orientation) const
737 {
738     Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
739     return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
740 }
741
742 bool PDFPlugin::isActive() const
743 {
744     if (Frame* coreFrame = m_frame->coreFrame()) {
745         if (Page* page = coreFrame->page())
746             return page->focusController().isActive();
747     }
748
749     return false;
750 }
751
752 bool PDFPlugin::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const
753 {
754     if (Frame* coreFrame = m_frame->coreFrame()) {
755         if (Page* page = coreFrame->page())
756             return page->settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting();
757     }
758
759     return false;
760 }
761
762 int PDFPlugin::scrollPosition(Scrollbar* scrollbar) const
763 {
764     if (scrollbar->orientation() == HorizontalScrollbar)
765         return m_scrollOffset.width();
766     if (scrollbar->orientation() == VerticalScrollbar)
767         return m_scrollOffset.height();
768     ASSERT_NOT_REACHED();
769     return 0;
770 }
771
772 IntPoint PDFPlugin::scrollPosition() const
773 {
774     return IntPoint(m_scrollOffset.width(), m_scrollOffset.height());
775 }
776
777 IntPoint PDFPlugin::minimumScrollPosition() const
778 {
779     return IntPoint();
780 }
781
782 IntPoint PDFPlugin::maximumScrollPosition() const
783 {
784     int horizontalScrollbarHeight = (m_horizontalScrollbar && !m_horizontalScrollbar->isOverlayScrollbar()) ? m_horizontalScrollbar->height() : 0;
785     int verticalScrollbarWidth = (m_verticalScrollbar && !m_verticalScrollbar->isOverlayScrollbar()) ? m_verticalScrollbar->width() : 0;
786
787     IntPoint maximumOffset(m_pdfDocumentSize.width() - m_size.width() + verticalScrollbarWidth, m_pdfDocumentSize.height() - m_size.height() + horizontalScrollbarHeight);
788     maximumOffset.clampNegativeToZero();
789     return maximumOffset;
790 }
791
792 void PDFPlugin::scrollbarStyleChanged(ScrollbarStyle style, bool forceUpdate)
793 {
794     if (!forceUpdate)
795         return;
796
797     // 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.
798     IntPoint newScrollOffset = IntPoint(m_scrollOffset).shrunkTo(maximumScrollPosition());
799     setScrollOffset(newScrollOffset);
800     
801     ScrollableArea::scrollbarStyleChanged(style, forceUpdate);
802     // As size of the content area changes, scrollbars may need to appear or to disappear.
803     updateScrollbars();
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     if (newScaleFactor != [m_pdfLayerController deviceScaleFactor])
1034         [m_pdfLayerController setDeviceScaleFactor:newScaleFactor];
1035 }
1036
1037 void PDFPlugin::contentsScaleFactorChanged(float)
1038 {
1039     updatePageAndDeviceScaleFactors();
1040 }
1041
1042 void PDFPlugin::computePageBoxes()
1043 {
1044     size_t pageCount = CGPDFDocumentGetNumberOfPages([m_pdfDocument documentRef]);
1045     for (size_t i = 0; i < pageCount; ++i) {
1046         CGPDFPageRef pdfPage = CGPDFDocumentGetPage([m_pdfDocument documentRef], i + 1);
1047         ASSERT(pdfPage);
1048
1049         CGRect box = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
1050         if (CGRectIsEmpty(box))
1051             box = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
1052         m_pageBoxes.append(IntRect(box));
1053     }
1054 }
1055
1056 void PDFPlugin::calculateSizes()
1057 {
1058     if ([pdfDocument() isLocked]) {
1059         setPDFDocumentSize(IntSize(0, 0));
1060         return;
1061     }
1062
1063     // FIXME: This should come straight from PDFKit.
1064     computePageBoxes();
1065
1066     setPDFDocumentSize(IntSize([m_pdfLayerController contentSizeRespectingZoom]));
1067 }
1068
1069 bool PDFPlugin::initialize(const Parameters& parameters)
1070 {
1071     m_sourceURL = parameters.url;
1072     if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty())
1073         controller()->loadURL(pdfDocumentRequestID, "GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false);
1074
1075     controller()->didInitializePlugin();
1076     return true;
1077 }
1078
1079 void PDFPlugin::destroy()
1080 {
1081     m_pdfLayerController.get().delegate = 0;
1082
1083     if (webFrame()) {
1084         if (FrameView* frameView = webFrame()->coreFrame()->view())
1085             frameView->removeScrollableArea(this);
1086     }
1087
1088     m_activeAnnotation = 0;
1089     m_annotationContainer = 0;
1090
1091     destroyScrollbar(HorizontalScrollbar);
1092     destroyScrollbar(VerticalScrollbar);
1093     
1094     [m_scrollCornerLayer removeFromSuperlayer];
1095     [m_contentLayer removeFromSuperlayer];
1096 }
1097
1098 void PDFPlugin::updateControlTints(GraphicsContext* graphicsContext)
1099 {
1100     ASSERT(graphicsContext->updatingControlTints());
1101
1102     if (m_horizontalScrollbar)
1103         m_horizontalScrollbar->invalidate();
1104     if (m_verticalScrollbar)
1105         m_verticalScrollbar->invalidate();
1106     invalidateScrollCorner(scrollCornerRect());
1107 }
1108
1109 void PDFPlugin::paintControlForLayerInContext(CALayer *layer, CGContextRef context)
1110 {
1111     GraphicsContext graphicsContext(context);
1112     GraphicsContextStateSaver stateSaver(graphicsContext);
1113     
1114     graphicsContext.setIsCALayerContext(true);
1115     
1116     if (layer == m_scrollCornerLayer) {
1117         IntRect scrollCornerRect = this->scrollCornerRect();
1118         graphicsContext.translate(-scrollCornerRect.x(), -scrollCornerRect.y());
1119         ScrollbarTheme::theme()->paintScrollCorner(0, &graphicsContext, scrollCornerRect);
1120         return;
1121     }
1122     
1123     Scrollbar* scrollbar = nullptr;
1124     
1125     if (layer == m_verticalScrollbarLayer)
1126         scrollbar = verticalScrollbar();
1127     else if (layer == m_horizontalScrollbarLayer)
1128         scrollbar = horizontalScrollbar();
1129
1130     if (!scrollbar)
1131         return;
1132     
1133     graphicsContext.translate(-scrollbar->x(), -scrollbar->y());
1134     scrollbar->paint(&graphicsContext, scrollbar->frameRect());
1135 }
1136
1137 PassRefPtr<ShareableBitmap> PDFPlugin::snapshot()
1138 {
1139     if (size().isEmpty())
1140         return nullptr;
1141
1142     float contentsScaleFactor = controller()->contentsScaleFactor();
1143     IntSize backingStoreSize = size();
1144     backingStoreSize.scale(contentsScaleFactor);
1145
1146     RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha);
1147     auto context = bitmap->createGraphicsContext();
1148
1149     context->scale(FloatSize(contentsScaleFactor, -contentsScaleFactor));
1150     context->translate(-m_scrollOffset.width(), -m_pdfDocumentSize.height() + m_scrollOffset.height());
1151
1152     [m_pdfLayerController snapshotInContext:context->platformContext()];
1153
1154     return bitmap.release();
1155 }
1156
1157 PlatformLayer* PDFPlugin::pluginLayer()
1158 {
1159     return m_containerLayer.get();
1160 }
1161
1162 IntPoint PDFPlugin::convertFromPluginToPDFView(const IntPoint& point) const
1163 {
1164     return IntPoint(point.x(), size().height() - point.y());
1165 }
1166
1167 IntPoint PDFPlugin::convertFromRootViewToPlugin(const IntPoint& point) const
1168 {
1169     return m_rootViewToPluginTransform.mapPoint(point);
1170 }
1171
1172 IntPoint PDFPlugin::convertFromPDFViewToRootView(const IntPoint& point) const
1173 {
1174     IntPoint pointInPluginCoordinates(point.x(), size().height() - point.y());
1175     return m_rootViewToPluginTransform.inverse().mapPoint(pointInPluginCoordinates);
1176 }
1177
1178 FloatRect PDFPlugin::convertFromPDFViewToScreen(const FloatRect& rect) const
1179 {
1180     FrameView* frameView = webFrame()->coreFrame()->view();
1181
1182     if (!frameView)
1183         return FloatRect();
1184
1185     FloatPoint originInPluginCoordinates(rect.x(), size().height() - rect.y() - rect.height());
1186     FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(FloatRect(originInPluginCoordinates, rect.size()));
1187
1188     return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
1189 }
1190
1191 IntRect PDFPlugin::boundsOnScreen() const
1192 {
1193     FrameView* frameView = webFrame()->coreFrame()->view();
1194
1195     if (!frameView)
1196         return IntRect();
1197
1198     FloatRect bounds = FloatRect(FloatPoint(), size());
1199     FloatRect rectInRootViewCoordinates = m_rootViewToPluginTransform.inverse().mapRect(bounds);
1200     return frameView->contentsToScreen(enclosingIntRect(rectInRootViewCoordinates));
1201 }
1202
1203 void PDFPlugin::geometryDidChange(const IntSize& pluginSize, const IntRect&, const AffineTransform& pluginToRootViewTransform)
1204 {
1205     if (size() == pluginSize && pluginView()->pageScaleFactor() == [m_pdfLayerController contentScaleFactor])
1206         return;
1207
1208     m_size = pluginSize;
1209     m_rootViewToPluginTransform = pluginToRootViewTransform.inverse();
1210     [m_pdfLayerController setFrameSize:pluginSize];
1211
1212     [CATransaction begin];
1213     [CATransaction setDisableActions:YES];
1214     CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
1215     transform = CATransform3DTranslate(transform, 0, -pluginSize.height(), 0);
1216     
1217     if (handlesPageScaleFactor()) {
1218         CGFloat magnification = pluginView()->pageScaleFactor() - [m_pdfLayerController contentScaleFactor];
1219
1220         // FIXME: Instead of m_lastMousePositionInPluginCoordinates, we should use the zoom origin from PluginView::setPageScaleFactor.
1221         if (magnification)
1222             [m_pdfLayerController magnifyWithMagnification:magnification atPoint:convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates) immediately:NO];
1223     } else {
1224         // If we don't handle page scale ourselves, we need to respect our parent page's
1225         // scale, which may have changed.
1226         updatePageAndDeviceScaleFactors();
1227     } 
1228
1229     calculateSizes();
1230     updateScrollbars();
1231
1232     if (m_activeAnnotation)
1233         m_activeAnnotation->updateGeometry();
1234
1235     [m_contentLayer setSublayerTransform:transform];
1236     [CATransaction commit];
1237 }
1238
1239 void PDFPlugin::frameDidFinishLoading(uint64_t)
1240 {
1241     ASSERT_NOT_REACHED();
1242 }
1243
1244 void PDFPlugin::frameDidFail(uint64_t, bool)
1245 {
1246     ASSERT_NOT_REACHED();
1247 }
1248
1249 void PDFPlugin::didEvaluateJavaScript(uint64_t, const WTF::String&)
1250 {
1251     ASSERT_NOT_REACHED();
1252 }
1253
1254     
1255 static NSUInteger modifierFlagsFromWebEvent(const WebEvent& event)
1256 {
1257     return (event.shiftKey() ? NSShiftKeyMask : 0)
1258         | (event.controlKey() ? NSControlKeyMask : 0)
1259         | (event.altKey() ? NSAlternateKeyMask : 0)
1260         | (event.metaKey() ? NSCommandKeyMask : 0);
1261 }
1262     
1263 static bool getEventTypeFromWebEvent(const WebEvent& event, NSEventType& eventType)
1264 {
1265     switch (event.type()) {
1266     case WebEvent::KeyDown:
1267         eventType = NSKeyDown;
1268         return true;
1269     case WebEvent::KeyUp:
1270         eventType = NSKeyUp;
1271         return true;
1272     case WebEvent::MouseDown:
1273         switch (static_cast<const WebMouseEvent&>(event).button()) {
1274         case WebMouseEvent::LeftButton:
1275             eventType = NSLeftMouseDown;
1276             return true;
1277         case WebMouseEvent::RightButton:
1278             eventType = NSRightMouseDown;
1279             return true;
1280         default:
1281             return false;
1282         }
1283     case WebEvent::MouseUp:
1284         switch (static_cast<const WebMouseEvent&>(event).button()) {
1285         case WebMouseEvent::LeftButton:
1286             eventType = NSLeftMouseUp;
1287             return true;
1288         case WebMouseEvent::RightButton:
1289             eventType = NSRightMouseUp;
1290             return true;
1291         default:
1292             return false;
1293         }
1294     case WebEvent::MouseMove:
1295         switch (static_cast<const WebMouseEvent&>(event).button()) {
1296         case WebMouseEvent::LeftButton:
1297             eventType = NSLeftMouseDragged;
1298             return true;
1299         case WebMouseEvent::RightButton:
1300             eventType = NSRightMouseDragged;
1301             return true;
1302         case WebMouseEvent::NoButton:
1303             eventType = NSMouseMoved;
1304             return true;
1305         default:
1306             return false;
1307         }
1308     default:
1309         return false;
1310     }
1311 }
1312     
1313 NSEvent *PDFPlugin::nsEventForWebMouseEvent(const WebMouseEvent& event)
1314 {
1315     m_lastMousePositionInPluginCoordinates = convertFromRootViewToPlugin(event.position());
1316
1317     IntPoint positionInPDFViewCoordinates(convertFromPluginToPDFView(m_lastMousePositionInPluginCoordinates));
1318
1319     NSEventType eventType;
1320
1321     if (!getEventTypeFromWebEvent(event, eventType))
1322         return 0;
1323
1324     NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1325
1326     return [NSEvent mouseEventWithType:eventType location:positionInPDFViewCoordinates modifierFlags:modifierFlags timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:event.clickCount() pressure:0];
1327 }
1328
1329 void PDFPlugin::updateCursor(const WebMouseEvent& event, UpdateCursorMode mode)
1330 {
1331     HitTestResult hitTestResult = None;
1332
1333     PDFSelection *selectionUnderMouse = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(event.position())];
1334     if (selectionUnderMouse && [[selectionUnderMouse string] length])
1335         hitTestResult = Text;
1336
1337     if (hitTestResult == m_lastHitTestResult && mode == UpdateIfNeeded)
1338         return;
1339
1340     webFrame()->page()->send(Messages::WebPageProxy::SetCursor(hitTestResult == Text ? iBeamCursor() : pointerCursor()));
1341     m_lastHitTestResult = hitTestResult;
1342 }
1343
1344 bool PDFPlugin::handleMouseEvent(const WebMouseEvent& event)
1345 {
1346     PlatformMouseEvent platformEvent = platform(event);
1347     IntPoint mousePosition = convertFromRootViewToPlugin(event.position());
1348
1349     m_lastMouseEvent = event;
1350
1351     RefPtr<Scrollbar> targetScrollbar;
1352     RefPtr<Scrollbar> targetScrollbarForLastMousePosition;
1353
1354     if (m_verticalScrollbarLayer) {
1355         IntRect verticalScrollbarFrame(m_verticalScrollbarLayer.get().frame);
1356         if (verticalScrollbarFrame.contains(mousePosition))
1357             targetScrollbar = verticalScrollbar();
1358         if (verticalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1359             targetScrollbarForLastMousePosition = verticalScrollbar();
1360     }
1361
1362     if (m_horizontalScrollbarLayer) {
1363         IntRect horizontalScrollbarFrame(m_horizontalScrollbarLayer.get().frame);
1364         if (horizontalScrollbarFrame.contains(mousePosition))
1365             targetScrollbar = horizontalScrollbar();
1366         if (horizontalScrollbarFrame.contains(m_lastMousePositionInPluginCoordinates))
1367             targetScrollbarForLastMousePosition = horizontalScrollbar();
1368     }
1369
1370     if (m_scrollCornerLayer && IntRect(m_scrollCornerLayer.get().frame).contains(mousePosition))
1371         return false;
1372
1373     if ([pdfDocument() isLocked])
1374         return false;
1375
1376     // Right-clicks and Control-clicks always call handleContextMenuEvent as well.
1377     if (event.button() == WebMouseEvent::RightButton || (event.button() == WebMouseEvent::LeftButton && event.controlKey()))
1378         return true;
1379
1380     NSEvent *nsEvent = nsEventForWebMouseEvent(event);
1381
1382     switch (event.type()) {
1383     case WebEvent::MouseMove:
1384         mouseMovedInContentArea();
1385         updateCursor(event);
1386
1387         if (targetScrollbar) {
1388             if (!targetScrollbarForLastMousePosition) {
1389                 targetScrollbar->mouseEntered();
1390                 return true;
1391             }
1392             return targetScrollbar->mouseMoved(platformEvent);
1393         }
1394
1395         if (!targetScrollbar && targetScrollbarForLastMousePosition)
1396             targetScrollbarForLastMousePosition->mouseExited();
1397
1398         switch (event.button()) {
1399         case WebMouseEvent::LeftButton:
1400             [m_pdfLayerController mouseDragged:nsEvent];
1401             return true;
1402         case WebMouseEvent::RightButton:
1403         case WebMouseEvent::MiddleButton:
1404             return false;
1405         case WebMouseEvent::NoButton:
1406             [m_pdfLayerController mouseMoved:nsEvent];
1407             return true;
1408         }
1409     case WebEvent::MouseDown:
1410         switch (event.button()) {
1411         case WebMouseEvent::LeftButton:
1412             if (targetScrollbar)
1413                 return targetScrollbar->mouseDown(platformEvent);
1414
1415             [m_pdfLayerController mouseDown:nsEvent];
1416             return true;
1417         case WebMouseEvent::RightButton:
1418             [m_pdfLayerController rightMouseDown:nsEvent];
1419             return true;
1420         case WebMouseEvent::MiddleButton:
1421         case WebMouseEvent::NoButton:
1422             return false;
1423         }
1424     case WebEvent::MouseUp:
1425         switch (event.button()) {
1426         case WebMouseEvent::LeftButton:
1427             if (targetScrollbar)
1428                 return targetScrollbar->mouseUp(platformEvent);
1429
1430             [m_pdfLayerController mouseUp:nsEvent];
1431             return true;
1432         case WebMouseEvent::RightButton:
1433         case WebMouseEvent::MiddleButton:
1434         case WebMouseEvent::NoButton:
1435             return false;
1436         }
1437     default:
1438         break;
1439     }
1440
1441     return false;
1442 }
1443
1444 bool PDFPlugin::handleMouseEnterEvent(const WebMouseEvent& event)
1445 {
1446     mouseEnteredContentArea();
1447     updateCursor(event, ForceUpdate);
1448     return false;
1449 }
1450
1451 bool PDFPlugin::handleMouseLeaveEvent(const WebMouseEvent&)
1452 {
1453     mouseExitedContentArea();
1454     return false;
1455 }
1456     
1457 bool PDFPlugin::showContextMenuAtPoint(const IntPoint& point)
1458 {
1459     FrameView* frameView = webFrame()->coreFrame()->view();
1460     IntPoint contentsPoint = frameView->contentsToRootView(point);
1461     WebMouseEvent event(WebEvent::MouseDown, WebMouseEvent::RightButton, contentsPoint, contentsPoint, 0, 0, 0, 1, static_cast<WebEvent::Modifiers>(0), monotonicallyIncreasingTime());
1462     return handleContextMenuEvent(event);
1463 }
1464
1465 bool PDFPlugin::handleContextMenuEvent(const WebMouseEvent& event)
1466 {
1467     FrameView* frameView = webFrame()->coreFrame()->view();
1468     IntPoint point = frameView->contentsToScreen(IntRect(frameView->windowToContents(event.position()), IntSize())).location();
1469     
1470     if (NSMenu *nsMenu = [m_pdfLayerController menuForEvent:nsEventForWebMouseEvent(event)]) {
1471         WKPopupContextMenu(nsMenu, point);
1472         return true;
1473     }
1474     
1475     return false;
1476 }
1477
1478 bool PDFPlugin::handleKeyboardEvent(const WebKeyboardEvent& event)
1479 {
1480     NSEventType eventType;
1481
1482     if (!getEventTypeFromWebEvent(event, eventType))
1483         return false;
1484
1485     NSUInteger modifierFlags = modifierFlagsFromWebEvent(event);
1486     
1487     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()];
1488     
1489     switch (event.type()) {
1490     case WebEvent::KeyDown:
1491         return [m_pdfLayerController keyDown:fakeEvent];
1492     default:
1493         return false;
1494     }
1495     
1496     return false;
1497 }
1498     
1499 bool PDFPlugin::handleEditingCommand(const String& commandName, const String& argument)
1500 {
1501     if (commandName == "copy")
1502         [m_pdfLayerController copySelection];
1503     else if (commandName == "selectAll")
1504         [m_pdfLayerController selectAll];
1505     else if (commandName == "takeFindStringFromSelection") {
1506         NSString *string = [m_pdfLayerController currentSelection].string;
1507         if (string.length)
1508             writeItemsToPasteboard(NSFindPboard, @[ [string dataUsingEncoding:NSUTF8StringEncoding] ], @[ NSPasteboardTypeString ]);
1509     }
1510
1511     return true;
1512 }
1513
1514 bool PDFPlugin::isEditingCommandEnabled(const String& commandName)
1515 {
1516     if (commandName == "copy" || commandName == "takeFindStringFromSelection")
1517         return [m_pdfLayerController currentSelection];
1518
1519     if (commandName == "selectAll")
1520         return true;
1521
1522     return false;
1523 }
1524
1525 void PDFPlugin::setScrollOffset(const IntPoint& offset)
1526 {
1527     m_scrollOffset = IntSize(offset.x(), offset.y());
1528
1529     [CATransaction begin];
1530     [m_pdfLayerController setScrollPosition:offset];
1531
1532     if (m_activeAnnotation)
1533         m_activeAnnotation->updateGeometry();
1534
1535     [CATransaction commit];
1536 }
1537
1538 void PDFPlugin::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
1539 {
1540     if (scrollbar == horizontalScrollbar())
1541         [m_horizontalScrollbarLayer setNeedsDisplay];
1542     else if (scrollbar == verticalScrollbar())
1543         [m_verticalScrollbarLayer setNeedsDisplay];
1544 }
1545
1546 void PDFPlugin::invalidateScrollCornerRect(const IntRect& rect)
1547 {
1548     [m_scrollCornerLayer setNeedsDisplay];
1549 }
1550
1551 bool PDFPlugin::isFullFramePlugin()
1552 {
1553     // <object> or <embed> plugins will appear to be in their parent frame, so we have to
1554     // check whether our frame's widget is exactly our PluginView.
1555     Document* document = webFrame()->coreFrame()->document();
1556     return document->isPluginDocument() && static_cast<PluginDocument*>(document)->pluginWidget() == pluginView();
1557 }
1558
1559 bool PDFPlugin::handlesPageScaleFactor()
1560 {
1561     return webFrame()->isMainFrame() && isFullFramePlugin();
1562 }
1563
1564 void PDFPlugin::clickedLink(NSURL *url)
1565 {
1566     URL coreURL = url;
1567     if (protocolIsJavaScript(coreURL))
1568         return;
1569
1570     Frame* frame = webFrame()->coreFrame();
1571
1572     RefPtr<Event> coreEvent;
1573     if (m_lastMouseEvent.type() != WebEvent::NoType)
1574         coreEvent = MouseEvent::create(eventNames().clickEvent, frame->document()->defaultView(), platform(m_lastMouseEvent), 0, 0);
1575
1576     frame->loader().urlSelected(coreURL, emptyString(), coreEvent.get(), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer);
1577 }
1578
1579 void PDFPlugin::setActiveAnnotation(PDFAnnotation *annotation)
1580 {
1581     if (!supportsForms())
1582         return;
1583
1584     if (m_activeAnnotation)
1585         m_activeAnnotation->commit();
1586
1587     if (annotation) {
1588         if ([annotation isKindOfClass:pdfAnnotationTextWidgetClass()] && static_cast<PDFAnnotationTextWidget *>(annotation).isReadOnly) {
1589             m_activeAnnotation = 0;
1590             return;
1591         }
1592
1593         m_activeAnnotation = PDFPluginAnnotation::create(annotation, m_pdfLayerController.get(), this);
1594         m_activeAnnotation->attach(m_annotationContainer.get());
1595     } else
1596         m_activeAnnotation = 0;
1597 }
1598
1599 bool PDFPlugin::supportsForms()
1600 {
1601     // 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.
1602     return isFullFramePlugin();
1603 }
1604
1605 void PDFPlugin::notifyContentScaleFactorChanged(CGFloat scaleFactor)
1606 {
1607     if (handlesPageScaleFactor())
1608         pluginView()->setPageScaleFactor(scaleFactor, IntPoint());
1609
1610     calculateSizes();
1611     updateScrollbars();
1612 }
1613
1614 void PDFPlugin::notifyDisplayModeChanged(int)
1615 {
1616     calculateSizes();
1617     updateScrollbars();
1618 }
1619
1620 PassRefPtr<SharedBuffer> PDFPlugin::liveResourceData() const
1621 {
1622     NSData *pdfData = liveData();
1623
1624     if (!pdfData)
1625         return 0;
1626
1627     return SharedBuffer::wrapNSData(pdfData);
1628 }
1629
1630 void PDFPlugin::saveToPDF()
1631 {
1632     // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1633     // PDFViewController does an NSBeep(), but that seems insufficient.
1634     if (!pdfDocument())
1635         return;
1636
1637     NSData *data = liveData();
1638     webFrame()->page()->savePDFToFileInDownloadsFolder(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length]);
1639 }
1640
1641 void PDFPlugin::openWithNativeApplication()
1642 {
1643     if (!m_temporaryPDFUUID) {
1644         // FIXME: We should probably notify the user that they can't save before the document is finished loading.
1645         // PDFViewController does an NSBeep(), but that seems insufficient.
1646         if (!pdfDocument())
1647             return;
1648
1649         NSData *data = liveData();
1650
1651         m_temporaryPDFUUID = WebCore::createCanonicalUUIDString();
1652         ASSERT(m_temporaryPDFUUID);
1653
1654         webFrame()->page()->savePDFToTemporaryFolderAndOpenWithNativeApplication(m_suggestedFilename, webFrame()->url(), static_cast<const unsigned char *>([data bytes]), [data length], m_temporaryPDFUUID);
1655         return;
1656     }
1657
1658     webFrame()->page()->send(Messages::WebPageProxy::OpenPDFFromTemporaryFolderWithNativeApplication(m_temporaryPDFUUID));
1659 }
1660
1661 void PDFPlugin::writeItemsToPasteboard(NSString *pasteboardName, NSArray *items, NSArray *types)
1662 {
1663     Vector<String> pasteboardTypes;
1664
1665     for (NSString *type in types)
1666         pasteboardTypes.append(type);
1667
1668     uint64_t newChangeCount;
1669     auto& webProcess = WebProcess::singleton();
1670     webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardTypes(pasteboardName, pasteboardTypes),
1671         Messages::WebPasteboardProxy::SetPasteboardTypes::Reply(newChangeCount), 0);
1672
1673     for (NSUInteger i = 0, count = items.count; i < count; ++i) {
1674         NSString *type = [types objectAtIndex:i];
1675         NSData *data = [items objectAtIndex:i];
1676
1677         // We don't expect the data for any items to be empty, but aren't completely sure.
1678         // Avoid crashing in the SharedMemory constructor in release builds if we're wrong.
1679         ASSERT(data.length);
1680         if (!data.length)
1681             continue;
1682
1683         if ([type isEqualToString:NSStringPboardType] || [type isEqualToString:NSPasteboardTypeString]) {
1684             RetainPtr<NSString> plainTextString = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
1685             webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardStringForType(pasteboardName, type, plainTextString.get()), Messages::WebPasteboardProxy::SetPasteboardStringForType::Reply(newChangeCount), 0);
1686         } else {
1687             RefPtr<SharedBuffer> buffer = SharedBuffer::wrapNSData(data);
1688
1689             if (!buffer)
1690                 continue;
1691
1692             SharedMemory::Handle handle;
1693             RefPtr<SharedMemory> sharedMemory = SharedMemory::create(buffer->size());
1694             memcpy(sharedMemory->data(), buffer->data(), buffer->size());
1695             sharedMemory->createHandle(handle, SharedMemory::ReadOnly);
1696             webProcess.parentProcessConnection()->sendSync(Messages::WebPasteboardProxy::SetPasteboardBufferForType(pasteboardName, type, handle, buffer->size()), Messages::WebPasteboardProxy::SetPasteboardBufferForType::Reply(newChangeCount), 0);
1697         }
1698     }
1699 }
1700
1701 void PDFPlugin::showDefinitionForAttributedString(NSAttributedString *string, CGPoint point)
1702 {
1703     DictionaryPopupInfo dictionaryPopupInfo;
1704     dictionaryPopupInfo.origin = convertFromPDFViewToRootView(IntPoint(point));
1705     dictionaryPopupInfo.attributedString.string = string;
1706
1707     webFrame()->page()->send(Messages::WebPageProxy::DidPerformDictionaryLookup(dictionaryPopupInfo));
1708 }
1709
1710 unsigned PDFPlugin::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1711 {
1712     if (!target.length())
1713         return 0;
1714
1715     int nsOptions = (options & FindOptionsCaseInsensitive) ? NSCaseInsensitiveSearch : 0;
1716
1717     return [[pdfDocument() findString:target withOptions:nsOptions] count];
1718 }
1719
1720 PDFSelection *PDFPlugin::nextMatchForString(const String& target, BOOL searchForward, BOOL caseSensitive, BOOL wrapSearch, PDFSelection *initialSelection, BOOL startInSelection)
1721 {
1722     if (!target.length())
1723         return nil;
1724
1725     NSStringCompareOptions options = 0;
1726     if (!searchForward)
1727         options |= NSBackwardsSearch;
1728
1729     if (!caseSensitive)
1730         options |= NSCaseInsensitiveSearch;
1731
1732     PDFDocument *document = pdfDocument().get();
1733
1734     PDFSelection *selectionForInitialSearch = [initialSelection copy];
1735     if (startInSelection) {
1736         // Initially we want to include the selected text in the search. So we must modify the starting search
1737         // selection to fit PDFDocument's search requirements: selection must have a length >= 1, begin before
1738         // the current selection (if searching forwards) or after (if searching backwards).
1739         int initialSelectionLength = [[initialSelection string] length];
1740         if (searchForward) {
1741             [selectionForInitialSearch extendSelectionAtStart:1];
1742             [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength];
1743         } else {
1744             [selectionForInitialSearch extendSelectionAtEnd:1];
1745             [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength];
1746         }
1747     }
1748
1749     PDFSelection *foundSelection = [document findString:target fromSelection:selectionForInitialSearch withOptions:options];
1750     [selectionForInitialSearch release];
1751
1752     // If we first searched in the selection, and we found the selection, search again from just past the selection.
1753     if (startInSelection && [foundSelection isEqual:initialSelection])
1754         foundSelection = [document findString:target fromSelection:initialSelection withOptions:options];
1755
1756     if (!foundSelection && wrapSearch)
1757         foundSelection = [document findString:target fromSelection:nil withOptions:options];
1758
1759     return foundSelection;
1760 }
1761
1762 bool PDFPlugin::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount)
1763 {
1764     BOOL searchForward = !(options & FindOptionsBackwards);
1765     BOOL caseSensitive = !(options & FindOptionsCaseInsensitive);
1766     BOOL wrapSearch = options & FindOptionsWrapAround;
1767
1768     unsigned matchCount;
1769     if (!maxMatchCount) {
1770         // If the max was zero, any result means we exceeded the max. We can skip computing the actual count.
1771         matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1772     } else {
1773         matchCount = countFindMatches(target, options, maxMatchCount);
1774         if (matchCount > maxMatchCount)
1775             matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
1776     }
1777
1778     if (target.isEmpty()) {
1779         PDFSelection* searchSelection = [m_pdfLayerController searchSelection];
1780         [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1781         [m_pdfLayerController setSearchSelection:searchSelection];
1782         m_lastFoundString = emptyString();
1783         return false;
1784     }
1785
1786     if (m_lastFoundString == target) {
1787         PDFSelection *selection = nextMatchForString(target, searchForward, caseSensitive, wrapSearch, [m_pdfLayerController searchSelection], NO);
1788         if (!selection)
1789             return false;
1790
1791         [m_pdfLayerController setSearchSelection:selection];
1792         [m_pdfLayerController gotoSelection:selection];
1793     } else {
1794         [m_pdfLayerController findString:target caseSensitive:caseSensitive highlightMatches:YES];
1795         m_lastFoundString = target;
1796     }
1797
1798     return matchCount > 0;
1799 }
1800
1801 bool PDFPlugin::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
1802 {
1803     IntPoint localPoint = convertFromRootViewToPlugin(roundedIntPoint(point));
1804     PDFSelection* lookupSelection = [m_pdfLayerController getSelectionForWordAtPoint:convertFromPluginToPDFView(localPoint)];
1805
1806     if ([[lookupSelection string] length])
1807         [m_pdfLayerController searchInDictionaryWithSelection:lookupSelection];
1808
1809     return true;
1810 }
1811
1812 void PDFPlugin::focusNextAnnotation()
1813 {
1814     [m_pdfLayerController activateNextAnnotation:false];
1815 }
1816
1817 void PDFPlugin::focusPreviousAnnotation()
1818 {
1819     [m_pdfLayerController activateNextAnnotation:true];
1820 }
1821
1822 void PDFPlugin::notifySelectionChanged(PDFSelection *)
1823 {
1824     webFrame()->page()->didChangeSelection();
1825 }
1826
1827 String PDFPlugin::getSelectionString() const
1828 {
1829     return [[m_pdfLayerController currentSelection] string];
1830 }
1831
1832 void PDFPlugin::performWebSearch(NSString *string)
1833 {
1834     webFrame()->page()->send(Messages::WebPageProxy::SearchTheWeb(string));
1835 }
1836
1837 void PDFPlugin::performSpotlightSearch(NSString *string)
1838 {
1839     webFrame()->page()->send(Messages::WebPageProxy::SearchWithSpotlight(string));
1840 }
1841
1842 bool PDFPlugin::handleWheelEvent(const WebWheelEvent& event)
1843 {
1844     PDFDisplayMode displayMode = [m_pdfLayerController displayMode];
1845
1846     if (displayMode == kPDFDisplaySinglePageContinuous || displayMode == kPDFDisplayTwoUpContinuous)
1847         return ScrollableArea::handleWheelEvent(platform(event));
1848
1849     NSUInteger currentPageIndex = [m_pdfLayerController currentPageIndex];
1850     bool inFirstPage = !currentPageIndex;
1851     bool inLastPage = [m_pdfLayerController lastPageIndex] == currentPageIndex;
1852
1853     bool atScrollTop = !scrollPosition().y();
1854     bool atScrollBottom = scrollPosition().y() == maximumScrollPosition().y();
1855
1856     bool inMomentumScroll = event.momentumPhase() != WebWheelEvent::PhaseNone;
1857
1858     int scrollMagnitudeThresholdForPageFlip = defaultScrollMagnitudeThresholdForPageFlip;
1859
1860     // Imprecise input devices should have a lower threshold so that "clicky" scroll wheels can flip pages.
1861     if (!event.hasPreciseScrollingDeltas())
1862         scrollMagnitudeThresholdForPageFlip = 0;
1863
1864     if (atScrollBottom && !inLastPage && event.delta().height() < 0) {
1865         if (event.delta().height() <= -scrollMagnitudeThresholdForPageFlip && !inMomentumScroll)
1866             [m_pdfLayerController gotoNextPage];
1867         return true;
1868     }
1869
1870     if (atScrollTop && !inFirstPage && event.delta().height() > 0) {
1871         if (event.delta().height() >= scrollMagnitudeThresholdForPageFlip && !inMomentumScroll) {
1872             [CATransaction begin];
1873             [m_pdfLayerController gotoPreviousPage];
1874             scrollToOffsetWithoutAnimation(maximumScrollPosition());
1875             [CATransaction commit];
1876         }
1877         return true;
1878     }
1879
1880     return ScrollableArea::handleWheelEvent(platform(event));
1881 }
1882
1883 NSData *PDFPlugin::liveData() const
1884 {
1885     if (m_activeAnnotation)
1886         m_activeAnnotation->commit();
1887
1888     // Save data straight from the resource instead of PDFKit if the document is
1889     // untouched by the user, so that PDFs which PDFKit can't display will still be downloadable.
1890     if (m_pdfDocumentWasMutated)
1891         return [m_pdfDocument dataRepresentation];
1892     
1893     return rawData();
1894 }
1895
1896 NSObject *PDFPlugin::accessibilityObject() const
1897 {
1898     return m_accessibilityObject.get();
1899 }
1900
1901 } // namespace WebKit
1902
1903 #endif // ENABLE(PDFKIT_PLUGIN)