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